generated from Exabyte-io/template-definitions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
template.js
159 lines (139 loc) · 5.53 KB
/
template.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import { allTemplates } from "@exabyte-io/application-flavors.js";
import { NamedInMemoryEntity } from "@mat3ra/code/dist/js/entity";
import { deepClone } from "@mat3ra/code/dist/js/utils";
import jinja from "swig";
import _ from "underscore";
import { ContextProviderRegistry } from "./context/registry";
export class Template extends NamedInMemoryEntity {
static providerRegistry = ContextProviderRegistry;
get isManuallyChanged() {
return this.prop("isManuallyChanged", false);
}
get content() {
return this.prop("content");
}
setContent(text) {
return this.setProp("content", text);
}
get rendered() {
return this.prop("rendered") || this.content;
}
setRendered(text) {
return this.setProp("rendered", text);
}
get applicationName() {
return this.prop("applicationName");
}
get executableName() {
return this.prop("executableName");
}
get contextProviders() {
return this.prop("contextProviders") || [];
}
addContextProvider(provider) {
this.setProp("contextProviders", this.contextProviders.push(provider));
}
removeContextProvider(provider) {
this.setProp(
"contextProviders",
this.contextProviders.filter(
(p) => p.name !== provider.name && p.domain !== provider.domain,
),
);
}
render(externalContext) {
const renderingContext = this.getRenderingContext(externalContext);
let template, rendered;
if (!this.isManuallyChanged) {
try {
template = jinja.compile(this.content);
// deepClone to pass JSON data without classes
rendered = template && template(this._cleanRenderingContext(renderingContext));
} catch (e) {
console.log(`Template is not compiled: ${e}`);
}
this.setRendered(this.isManuallyChanged ? rendered : rendered || this.content);
}
}
getRenderedJSON(context = this.context) {
this.render(context);
return this.toJSON();
}
// Remove "bulky" items and JSON stringify before passing it to rendering engine (eg. jinja) to compile.
// This way the context should still be passed in full to contextProviders, but not to final text template.
// eslint-disable-next-line class-methods-use-this
_cleanRenderingContext(object) {
const { job, ...clone } = object;
return deepClone(clone);
}
static fromFlavor(appName, execName, inputName) {
const filtered = allTemplates.filter(
(temp) =>
temp.applicationName === appName &&
temp.executableName === execName &&
temp.name === inputName,
);
if (filtered.length !== 1) {
console.log(
`found ${filtered.length} templates for app=${appName} exec=${execName} name=${inputName} expected 1`,
);
}
return new Template(filtered[0]);
}
/*
* @summary Initializes context provider class instances. `providerContext` is used to pass the data about any
* previously stored values. That is if data was previously saved in database, the context provider
* shall receive it on initialization through providerContext and prioritize this value over the default.
*/
getContextProvidersAsClassInstances(providerContext) {
const me = this;
return this.contextProviders.map((p) => {
const { constructor, config } =
me.constructor.providerRegistry.findProviderInstanceByName(p.name);
const clsInstance = new constructor({
...config,
context: providerContext,
});
return clsInstance;
});
}
/*
* @summary Extracts the the data from all context providers for further use during render.
*/
getDataFromProvidersForRenderingContext(providerContext) {
const result = {};
this.getContextProvidersAsClassInstances(providerContext).forEach((contextProvider) => {
const context = contextProvider.yieldDataForRendering();
Object.keys(context).forEach((key) => {
// merge context keys if they are objects otherwise override them.
result[key] = _.isObject(result[key])
? { ...result[key], ...context[key] }
: context[key];
});
});
return result;
}
/*
* @summary Extracts the the data from all context providers for further save in persistent context.
*/
// TODO: optimize logic to prevent re-initializing the context provider classes again below, reuse above function
getDataFromProvidersForPersistentContext(providerContext) {
const result = {};
this.getContextProvidersAsClassInstances(providerContext).forEach((contextProvider) => {
// only save in the persistent context the data from providers that were edited (or able to be edited)
Object.assign(result, contextProvider.isEdited ? contextProvider.yieldData() : {});
});
return result;
}
/*
@summary Combines rendering context (in order of preference):
* - context from templates initialized with external context
* - "external" context and
*/
getRenderingContext(externalContext) {
return {
...externalContext,
...this.getDataFromProvidersForRenderingContext(externalContext),
};
}
}