@gudhub/core 1.1.125 → 1.1.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GUDHUB/Utils/AppsTemplateService/AppsTemplateService.js +351 -406
- package/GUDHUB/Utils/FIleHelper/FileHelper.js +107 -57
- package/GUDHUB/Utils/Utils.js +5 -5
- package/GUDHUB/gudhub-https-service.js +78 -40
- package/GUDHUB/utils.js +2 -2
- package/package.json +1 -1
- package/umd/library.min.js +6 -6
- package/umd/library.min.js.map +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { GudHub } from "../../gudhub.js";
|
|
2
|
+
|
|
1
3
|
export default class AppsTemplateService {
|
|
2
4
|
constructor(gudhub) {
|
|
3
5
|
this.gudhub = gudhub;
|
|
@@ -6,370 +8,332 @@ export default class AppsTemplateService {
|
|
|
6
8
|
fields: [],
|
|
7
9
|
items: [],
|
|
8
10
|
views: []
|
|
9
|
-
}
|
|
11
|
+
};
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
createAppsFromTemplate(pack,
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
progressCallback ? progressCallback.call(this, {
|
|
21
|
-
percent: currentStep * stepPercents,
|
|
22
|
-
status
|
|
23
|
-
}) : null;
|
|
24
|
-
}, installFromMaster).then(success => {
|
|
25
|
-
self.createItems(success, maxNumberOfInsstalledItems, (status) => {
|
|
26
|
-
if (typeof status === 'string') {
|
|
27
|
-
currentStep += 1;
|
|
28
|
-
progressCallback ? progressCallback.call(this, {
|
|
29
|
-
percent: currentStep * stepPercents,
|
|
30
|
-
status
|
|
31
|
-
}) : null;
|
|
32
|
-
} else if (typeof status === 'object') {
|
|
33
|
-
progressCallback ? progressCallback.call(this, {
|
|
34
|
-
status: 'Done',
|
|
35
|
-
apps: status
|
|
36
|
-
}) : null;
|
|
37
|
-
resolve();
|
|
38
|
-
}
|
|
39
|
-
}, installFromMaster)
|
|
14
|
+
async createAppsFromTemplate(pack, remoteServerUrl, remoteServerAuthKey, isInstallFromRemoteServer, maxNumberOfInstalledItems, progressCallback) {
|
|
15
|
+
const stepsCount = pack.apps.length * 6;
|
|
16
|
+
const stepPercents = 100 / stepsCount;
|
|
17
|
+
let currentStep = 0;
|
|
18
|
+
|
|
19
|
+
const remoteServerGudHubInstance = isInstallFromRemoteServer
|
|
20
|
+
? new GudHub(remoteServerAuthKey, {
|
|
21
|
+
server_url: `${remoteServerUrl}/GudHub`
|
|
40
22
|
})
|
|
41
|
-
|
|
23
|
+
: null;
|
|
24
|
+
|
|
25
|
+
const appsList = await remoteServerGudHubInstance.getAppsList();
|
|
26
|
+
|
|
27
|
+
const onAppStep = (status) => {
|
|
28
|
+
currentStep += 1;
|
|
29
|
+
progressCallback?.({ percent: currentStep * stepPercents, status });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const onItemStep = (status) => {
|
|
33
|
+
if (typeof status === 'string') {
|
|
34
|
+
currentStep += 1;
|
|
35
|
+
progressCallback?.({ percent: currentStep * stepPercents, status });
|
|
36
|
+
} else if (typeof status === 'object') {
|
|
37
|
+
progressCallback?.({ status: 'Done', apps: status });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const createdApps = await this._createApps(pack, remoteServerUrl, remoteServerAuthKey, isInstallFromRemoteServer, remoteServerGudHubInstance, onAppStep);
|
|
42
|
+
await this._createItems(createdApps, remoteServerUrl, remoteServerAuthKey, isInstallFromRemoteServer, remoteServerGudHubInstance, maxNumberOfInstalledItems, onItemStep);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let promises = [];
|
|
45
|
+
// Fetches source apps, clones them as new ones and builds ID mapping between old and new entities
|
|
46
|
+
async _createApps(pack, remoteServerUrl, remoteServerAuthKey, isInstallFromRemoteServer, remoteServerGudHubInstance, callback) {
|
|
47
|
+
const source_apps = {};
|
|
48
|
+
const created_apps = [];
|
|
49
|
+
const updated_apps = [];
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
let accesstoken = null;
|
|
52
|
+
|
|
53
|
+
if (isInstallFromRemoteServer) {
|
|
54
|
+
const response = await this.gudhub.req.axiosRequest({
|
|
52
55
|
method: 'POST',
|
|
53
|
-
url:
|
|
54
|
-
form: {
|
|
55
|
-
auth_key: '3HMOtCbC0M/1/1e4y4Uxo/Kh/aFN4V5LG2//5ixx7TZyiUfMb7IHAAHCj9PsLrOSDrZuuWkbX4BIX23f51H+eA=='
|
|
56
|
-
}
|
|
56
|
+
url: `${remoteServerUrl}/GudHub/auth/login`,
|
|
57
|
+
form: { auth_key: remoteServerAuthKey }
|
|
57
58
|
});
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let result_app;
|
|
62
|
-
if(installFromMaster) {
|
|
63
|
-
result_app = await self.gudhub.req.axiosRequest({ url: `https://app.gudhub.com/GudHub/api/app/get?token=${accesstoken}&app_id=${self._searchOldAppId(app.app_id, self.appsConnectingMap.apps)}`, method: 'GET' });
|
|
64
|
-
} else {
|
|
65
|
-
result_app = await self.gudhub.getApp(self._searchOldAppId(app.app_id, self.appsConnectingMap.apps));
|
|
66
|
-
}
|
|
67
|
-
callback ? callback.call(this, `GET APP: ${result_app.app_name} (Items steps)`) : null;
|
|
68
|
-
let source_app = JSON.parse(JSON.stringify(result_app));
|
|
69
|
-
let items = source_app.items_list.map(item => {
|
|
70
|
-
return {
|
|
71
|
-
index_number: item.index_number,
|
|
72
|
-
item_id: 0,
|
|
73
|
-
fields: []
|
|
74
|
-
}
|
|
75
|
-
}).filter((item, index) => index <= MAX_NUMBER_OF_INSTALLED_ITEMS);
|
|
76
|
-
self.gudhub.addNewItems(app.app_id, items).then(items => {
|
|
77
|
-
callback ? callback.call(this, `ADD NEW ITEMS: ${result_app.app_name} (Items steps)`) : null;
|
|
60
|
+
accesstoken = response.accesstoken;
|
|
61
|
+
}
|
|
78
62
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self._addFieldValue(source_app.items_list, app.items_list, self.appsConnectingMap);
|
|
63
|
+
for (const app_id of pack.apps) {
|
|
64
|
+
let result_app;
|
|
82
65
|
|
|
83
|
-
|
|
66
|
+
if (isInstallFromRemoteServer) {
|
|
67
|
+
result_app = await this.gudhub.req.axiosRequest({
|
|
68
|
+
url: `${remoteServerUrl}/GudHub/api/app/get?token=${accesstoken}&app_id=${app_id}`,
|
|
69
|
+
method: 'GET'
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
result_app = await this.gudhub.getApp(app_id);
|
|
73
|
+
}
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
75
|
+
if (!result_app) {
|
|
76
|
+
throw new Error(`Failed to get app: ${app_id}`);
|
|
77
|
+
}
|
|
89
78
|
|
|
90
|
-
|
|
79
|
+
callback?.(`GET APP: ${result_app.app_name} (App steps)`);
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
81
|
+
const source_app = JSON.parse(JSON.stringify(result_app));
|
|
82
|
+
source_apps[source_app.app_id] = source_app;
|
|
83
|
+
|
|
84
|
+
let appTemplate = this._prepareAppTemplate(source_app);
|
|
85
|
+
appTemplate.privacy = 1;
|
|
86
|
+
|
|
87
|
+
if (pack.data_to_change) {
|
|
88
|
+
if (pack.data_to_change.name) appTemplate.app_name = pack.data_to_change.name;
|
|
89
|
+
if (pack.data_to_change.icon) appTemplate.icon = pack.data_to_change.icon;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this._resetViewsIds(appTemplate);
|
|
93
|
+
|
|
94
|
+
const created_app = await this.gudhub.createNewApp(appTemplate);
|
|
95
|
+
callback?.(`CREATE NEW APP: ${result_app.app_name} (App steps)`);
|
|
96
|
+
|
|
97
|
+
created_apps.push(created_app);
|
|
98
|
+
this._mapApp(source_app, created_app, this.appsConnectingMap);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this._connectApps(created_apps, this.appsConnectingMap, source_apps);
|
|
102
|
+
|
|
103
|
+
for (const created_app of created_apps) {
|
|
104
|
+
const updated_app = await this.gudhub.updateApp(created_app);
|
|
105
|
+
callback?.(`UPDATE APP: ${updated_app.app_name} (App steps)`);
|
|
106
|
+
updated_apps.push(updated_app);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return updated_apps;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Creates items for each app and restores their data with proper ID remapping
|
|
113
|
+
async _createItems(create_apps, remoteServerUrl, remoteServerAuthKey, isInstallFromRemoteServer, remoteServerGudHubInstance, maxNumberOfInstalledItems, callback) {
|
|
114
|
+
const MAX_NUMBER_OF_INSTALLED_ITEMS = maxNumberOfInstalledItems ?? 100000;
|
|
115
|
+
|
|
116
|
+
let accesstoken = null;
|
|
117
|
+
|
|
118
|
+
if (isInstallFromRemoteServer) {
|
|
119
|
+
const response = await this.gudhub.req.axiosRequest({
|
|
120
|
+
method: 'POST',
|
|
121
|
+
url: `${remoteServerUrl}/GudHub/auth/login`,
|
|
122
|
+
form: { auth_key: remoteServerAuthKey }
|
|
103
123
|
});
|
|
124
|
+
accesstoken = response.accesstoken;
|
|
125
|
+
}
|
|
104
126
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
promises.push(new Promise(resolve => {
|
|
113
|
-
let newItems = created_app.items_list.map(item => {
|
|
114
|
-
item.fields.forEach(field => {
|
|
115
|
-
delete field.data_id;
|
|
116
|
-
});
|
|
117
|
-
return item;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
self.gudhub.updateItems(created_app.app_id, newItems).then(() => {
|
|
121
|
-
callback ? callback.call(this, `REPLACE VALUE: ${created_app.app_name} (Items steps)`) : null;
|
|
122
|
-
resolve();
|
|
123
|
-
})
|
|
124
|
-
}));
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
Promise.all(promises).then(() => {
|
|
128
|
-
callback.call(this, createsItems);
|
|
129
|
-
resolve();
|
|
127
|
+
const createdItems = await Promise.all(create_apps.map(async (app) => {
|
|
128
|
+
let result_app;
|
|
129
|
+
|
|
130
|
+
if (isInstallFromRemoteServer) {
|
|
131
|
+
result_app = await this.gudhub.req.axiosRequest({
|
|
132
|
+
url: `${remoteServerUrl}/GudHub/api/app/get?token=${accesstoken}&app_id=${this._searchOldAppId(app.app_id, this.appsConnectingMap.apps)}`,
|
|
133
|
+
method: 'GET'
|
|
130
134
|
});
|
|
135
|
+
} else {
|
|
136
|
+
result_app = await this.gudhub.getApp(this._searchOldAppId(app.app_id, this.appsConnectingMap.apps));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!result_app) {
|
|
140
|
+
throw new Error(`Failed to get app in createItems: ${app.app_id}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
callback?.(`GET APP: ${result_app.app_name} (Items steps)`);
|
|
144
|
+
|
|
145
|
+
const source_app = JSON.parse(JSON.stringify(result_app));
|
|
146
|
+
const items = source_app.items_list
|
|
147
|
+
.map(item => ({ index_number: item.index_number, item_id: 0, fields: [] }))
|
|
148
|
+
.filter((_, index) => index <= MAX_NUMBER_OF_INSTALLED_ITEMS);
|
|
149
|
+
|
|
150
|
+
const newItems = await this.gudhub.addNewItems(app.app_id, items);
|
|
151
|
+
callback?.(`ADD NEW ITEMS: ${result_app.app_name} (Items steps)`);
|
|
152
|
+
|
|
153
|
+
app.items_list = newItems;
|
|
154
|
+
this._updateMap(app.items_list, source_app.items_list);
|
|
155
|
+
this._addFieldValue(source_app.items_list, app.items_list, this.appsConnectingMap);
|
|
156
|
+
|
|
157
|
+
return app;
|
|
158
|
+
}));
|
|
159
|
+
|
|
160
|
+
for (const app of createdItems) {
|
|
161
|
+
for (const item of app.items_list) {
|
|
162
|
+
for (const field_item of item.fields) {
|
|
163
|
+
for (const field_map of this.appsConnectingMap.fields) {
|
|
164
|
+
if (field_item.field_id === field_map.old_field_id) {
|
|
165
|
+
field_item.field_id = field_map.new_field_id;
|
|
166
|
+
field_item.element_id = field_map.new_field_id;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
131
172
|
|
|
173
|
+
this._deleteField(createdItems);
|
|
174
|
+
await this._replaceValue(
|
|
175
|
+
createdItems,
|
|
176
|
+
this.appsConnectingMap,
|
|
177
|
+
isInstallFromRemoteServer,
|
|
178
|
+
remoteServerGudHubInstance
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await Promise.all(createdItems.map(async (created_app) => {
|
|
182
|
+
const newItems = created_app.items_list.map(item => {
|
|
183
|
+
item.fields.forEach(field => { delete field.data_id; });
|
|
184
|
+
return item;
|
|
132
185
|
});
|
|
133
|
-
|
|
134
|
-
|
|
186
|
+
|
|
187
|
+
await this.gudhub.updateItems(created_app.app_id, newItems);
|
|
188
|
+
callback?.(`REPLACE VALUE: ${created_app.app_name} (Items steps)`);
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
callback?.(createdItems);
|
|
135
192
|
}
|
|
136
193
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
194
|
+
// Ensures items only contain fields that still exist in the app schema
|
|
195
|
+
_deleteField(apps) {
|
|
196
|
+
for (const app of apps) {
|
|
197
|
+
for (const item of app.items_list) {
|
|
198
|
+
item.fields = item.fields.filter(field =>
|
|
199
|
+
app.field_list.findIndex(f => f.field_id == field.field_id) > -1
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
145
203
|
}
|
|
146
204
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
205
|
+
// Handles post-processing of field values (files, documents, references)
|
|
206
|
+
async _replaceValue(apps, map, isInstallFromRemoteServer, remoteServerGudHubInstance) {
|
|
207
|
+
const allPromises = [];
|
|
208
|
+
const mapAppIds = map?.apps;
|
|
151
209
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
210
|
+
for (const app of apps) {
|
|
211
|
+
for (const field_of_list of app.field_list) {
|
|
212
|
+
if (field_of_list.data_type === 'item_ref') {
|
|
213
|
+
this._replaceValueItemRef(app, field_of_list, map);
|
|
214
|
+
}
|
|
157
215
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
type: field_of_list.data_type
|
|
164
|
-
});
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (data.getTemplate().constructor == 'file') {
|
|
168
|
-
return self.gudhub.util.fileInstallerHelper(app.app_id, app.items_list, field_of_list.field_id).then(result => {
|
|
169
|
-
resolve(result);
|
|
170
|
-
});
|
|
171
|
-
} else if (data.getTemplate().constructor == 'document') {
|
|
172
|
-
self.documentInstallerHelper(app.app_id, app.items_list, field_of_list.field_id).then(result => {
|
|
173
|
-
resolve(data);
|
|
174
|
-
})
|
|
175
|
-
} else {
|
|
176
|
-
resolve(data);
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
}))
|
|
180
|
-
})
|
|
181
|
-
});
|
|
216
|
+
allPromises.push(
|
|
217
|
+
this.gudhub.ghconstructor.getInstance(field_of_list.data_type).then(async (data) => {
|
|
218
|
+
if (typeof data === 'undefined') {
|
|
219
|
+
return { type: field_of_list.data_type };
|
|
220
|
+
}
|
|
182
221
|
|
|
183
|
-
|
|
222
|
+
if (data.getTemplate().constructor === 'file') {
|
|
223
|
+
return this.gudhub.util.fileInstallerHelper(
|
|
224
|
+
mapAppIds,
|
|
225
|
+
app.app_id,
|
|
226
|
+
app.items_list,
|
|
227
|
+
field_of_list.field_id,
|
|
228
|
+
isInstallFromRemoteServer,
|
|
229
|
+
remoteServerGudHubInstance
|
|
230
|
+
);
|
|
231
|
+
}
|
|
184
232
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
233
|
+
if (data.getTemplate().constructor === 'document') {
|
|
234
|
+
await this._documentInstallerHelper(app.app_id, app.items_list, field_of_list.field_id);
|
|
235
|
+
return data;
|
|
236
|
+
}
|
|
188
237
|
|
|
189
|
-
|
|
190
|
-
|
|
238
|
+
return data;
|
|
239
|
+
})
|
|
240
|
+
);
|
|
191
241
|
}
|
|
242
|
+
}
|
|
192
243
|
|
|
193
|
-
|
|
244
|
+
if (allPromises.length > 0) {
|
|
245
|
+
return Promise.all(allPromises);
|
|
246
|
+
}
|
|
194
247
|
}
|
|
195
248
|
|
|
196
|
-
|
|
197
|
-
|
|
249
|
+
// Clones documents so new items don’t reference original app data
|
|
250
|
+
async _documentInstallerHelper(appId, items, elementId) {
|
|
198
251
|
const itemsWithClonedDocument = [];
|
|
199
252
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const itemId = item.item_id;
|
|
203
|
-
const field = item.fields.find(field => field.element_id === elementId);
|
|
204
|
-
|
|
205
|
-
if (field && field.field_value && field.field_value.length > 0) {
|
|
206
|
-
const document = await self.gudhub.getDocument({
|
|
207
|
-
_id: field.field_value
|
|
208
|
-
});
|
|
253
|
+
for (const item of items) {
|
|
254
|
+
const field = item.fields.find(field => field.element_id === elementId);
|
|
209
255
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const newDocument = await self.gudhub.createDocument({
|
|
213
|
-
app_id: appId,
|
|
214
|
-
item_id: itemId,
|
|
215
|
-
element_id: elementId,
|
|
216
|
-
data: document.data
|
|
217
|
-
});
|
|
256
|
+
if (field?.field_value?.length > 0) {
|
|
257
|
+
const document = await this.gudhub.getDocument({ _id: field.field_value });
|
|
218
258
|
|
|
219
|
-
|
|
259
|
+
if (document?.data) {
|
|
260
|
+
const newDocument = await this.gudhub.createDocument({
|
|
261
|
+
app_id: appId,
|
|
262
|
+
item_id: item.item_id,
|
|
263
|
+
element_id: elementId,
|
|
264
|
+
data: document.data
|
|
265
|
+
});
|
|
220
266
|
|
|
221
|
-
|
|
222
|
-
|
|
267
|
+
field.field_value = newDocument._id;
|
|
268
|
+
itemsWithClonedDocument.push(item);
|
|
223
269
|
}
|
|
224
270
|
}
|
|
225
|
-
|
|
226
|
-
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return itemsWithClonedDocument;
|
|
227
274
|
}
|
|
228
275
|
|
|
276
|
+
// Rewrites item_ref values to point to newly created apps/items instead of old ones
|
|
229
277
|
_replaceValueItemRef(app, field_of_list, map) {
|
|
230
|
-
app.items_list
|
|
231
|
-
item.fields
|
|
278
|
+
for (const item of app.items_list) {
|
|
279
|
+
for (const field_of_item of item.fields) {
|
|
232
280
|
if (field_of_item.field_id === field_of_list.field_id && field_of_item.field_value) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
arr_values.forEach((one_value, key) => {
|
|
236
|
-
let value = one_value.split('.');
|
|
237
|
-
|
|
238
|
-
map.apps.forEach(app_ids => {
|
|
239
|
-
if (value[0] == app_ids.old_app_id) {
|
|
240
|
-
value[0] = app_ids.new_app_id;
|
|
241
|
-
}
|
|
242
|
-
});
|
|
281
|
+
const arr_values = field_of_item.field_value.split(',').map(one_value => {
|
|
282
|
+
const value = one_value.split('.');
|
|
243
283
|
|
|
244
|
-
map.
|
|
245
|
-
if (value[
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
});
|
|
284
|
+
for (const app_ids of map.apps) {
|
|
285
|
+
if (value[0] == app_ids.old_app_id) value[0] = app_ids.new_app_id;
|
|
286
|
+
}
|
|
249
287
|
|
|
250
|
-
|
|
288
|
+
for (const item_ids of map.items) {
|
|
289
|
+
if (value[1] == item_ids.old_item_id) value[1] = item_ids.new_item_id;
|
|
290
|
+
}
|
|
251
291
|
|
|
292
|
+
return value.join('.');
|
|
252
293
|
});
|
|
253
294
|
|
|
254
295
|
field_of_item.field_value = arr_values.join(',');
|
|
255
|
-
|
|
256
296
|
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
259
299
|
}
|
|
260
300
|
|
|
301
|
+
// Copies field values from source items into newly created items
|
|
261
302
|
_addFieldValue(source_app_items, create_items, appsConnectingMap) {
|
|
262
|
-
|
|
263
|
-
appsConnectingMap.items
|
|
303
|
+
for (const new_item of create_items) {
|
|
304
|
+
for (const item_of_map of appsConnectingMap.items) {
|
|
264
305
|
if (new_item.item_id === item_of_map.new_item_id) {
|
|
265
|
-
|
|
306
|
+
for (const old_item of source_app_items) {
|
|
266
307
|
if (item_of_map.old_item_id === old_item.item_id) {
|
|
267
308
|
new_item.fields = old_item.fields;
|
|
268
309
|
}
|
|
269
|
-
}
|
|
310
|
+
}
|
|
270
311
|
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
273
314
|
}
|
|
274
315
|
|
|
316
|
+
// Builds mapping between old and new item IDs (based on index consistency)
|
|
275
317
|
_updateMap(new_items, old_items) {
|
|
276
|
-
|
|
277
|
-
|
|
318
|
+
for (const old_item of old_items) {
|
|
319
|
+
for (const new_item of new_items) {
|
|
278
320
|
if (old_item.index_number === new_item.index_number) {
|
|
279
321
|
this.appsConnectingMap.items.push({
|
|
280
322
|
old_item_id: old_item.item_id,
|
|
281
323
|
new_item_id: new_item.item_id
|
|
282
|
-
})
|
|
324
|
+
});
|
|
283
325
|
}
|
|
284
|
-
})
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
_searchOldAppId(app_id, apps) {
|
|
289
|
-
let findId = 0;
|
|
290
|
-
|
|
291
|
-
apps.forEach(value => {
|
|
292
|
-
if (value.new_app_id === app_id) {
|
|
293
|
-
findId = value.old_app_id;
|
|
294
326
|
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
return findId;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
createApps(pack, callback, installFromMaster) {
|
|
301
|
-
const self = this;
|
|
302
|
-
|
|
303
|
-
let progress = {
|
|
304
|
-
step_size: 100 / (pack.apps.length * 3),
|
|
305
|
-
apps: []
|
|
306
327
|
}
|
|
328
|
+
}
|
|
307
329
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
let updated_apps = [];
|
|
312
|
-
|
|
313
|
-
const { accesstoken } = await self.gudhub.req.axiosRequest({
|
|
314
|
-
method: 'POST',
|
|
315
|
-
url: 'https://app.gudhub.com/GudHub/auth/login',
|
|
316
|
-
form: {
|
|
317
|
-
auth_key: '3HMOtCbC0M/1/1e4y4Uxo/Kh/aFN4V5LG2//5ixx7TZyiUfMb7IHAAHCj9PsLrOSDrZuuWkbX4BIX23f51H+eA=='
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
for(const app_id of pack.apps) {
|
|
322
|
-
let result_app;
|
|
323
|
-
|
|
324
|
-
if(installFromMaster) {
|
|
325
|
-
result_app = await self.gudhub.req.axiosRequest({ url: `https://app.gudhub.com/GudHub/api/app/get?token=${accesstoken}&app_id=${app_id}`, method: 'GET' });
|
|
326
|
-
} else {
|
|
327
|
-
result_app = await self.gudhub.getApp(app_id);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
callback ? callback.call(this, `GET APP: ${result_app.app_name} (App steps)`) : null;
|
|
331
|
-
let source_app = JSON.parse(JSON.stringify(result_app));
|
|
332
|
-
source_apps[source_app.app_id] = source_app;
|
|
333
|
-
progress.apps.push(source_app.icon);
|
|
334
|
-
|
|
335
|
-
let appTemplate = self.prepareAppTemplate(source_app);
|
|
336
|
-
appTemplate.app_name = appTemplate.app_name;
|
|
337
|
-
appTemplate.privacy = 1;
|
|
338
|
-
if(pack.data_to_change) {
|
|
339
|
-
if(pack.data_to_change.name) {
|
|
340
|
-
appTemplate.app_name = pack.data_to_change.name;
|
|
341
|
-
}
|
|
342
|
-
if(pack.data_to_change.icon) {
|
|
343
|
-
appTemplate.icon = pack.data_to_change.icon;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
self.resetViewsIds(appTemplate);
|
|
347
|
-
|
|
348
|
-
self.gudhub.createNewApp(appTemplate).then(created_app => {
|
|
349
|
-
callback ? callback.call(this, `CREATE NEW APP: ${result_app.app_name} (App steps)`) : null;
|
|
350
|
-
created_apps.push(created_app);
|
|
351
|
-
self.mapApp(source_app, created_app, self.appsConnectingMap);
|
|
352
|
-
|
|
353
|
-
if (pack.apps.length == created_apps.length) {
|
|
354
|
-
self.connectApps(created_apps, self.appsConnectingMap, source_apps);
|
|
355
|
-
|
|
356
|
-
created_apps.forEach(created_app => {
|
|
357
|
-
self.gudhub.updateApp(created_app).then(updated_app => {
|
|
358
|
-
callback ? callback.call(this, `UPDATE APP: ${result_app.app_name} (App steps)`) : null;
|
|
359
|
-
updated_apps.push(updated_app);
|
|
360
|
-
|
|
361
|
-
if (pack.apps.length == updated_apps.length) {
|
|
362
|
-
resolve(updated_apps);
|
|
363
|
-
}
|
|
364
|
-
})
|
|
365
|
-
})
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
});
|
|
330
|
+
// Resolves original app ID from mapping (used when syncing data)
|
|
331
|
+
_searchOldAppId(app_id, apps) {
|
|
332
|
+
return apps.find(value => value.new_app_id === app_id)?.old_app_id ?? 0;
|
|
370
333
|
}
|
|
371
334
|
|
|
372
|
-
|
|
335
|
+
// Stores mapping between old/new apps, fields and views for later ID replacement
|
|
336
|
+
_mapApp(base_app, new_app, appsConnectingMap) {
|
|
373
337
|
appsConnectingMap.apps.push({
|
|
374
338
|
old_app_id: base_app.app_id,
|
|
375
339
|
new_app_id: new_app.app_id,
|
|
@@ -377,196 +341,179 @@ export default class AppsTemplateService {
|
|
|
377
341
|
new_view_init: new_app.view_init
|
|
378
342
|
});
|
|
379
343
|
|
|
380
|
-
base_app.field_list
|
|
381
|
-
new_app.field_list
|
|
382
|
-
if (old_field.name_space
|
|
344
|
+
for (const old_field of base_app.field_list) {
|
|
345
|
+
for (const new_field of new_app.field_list) {
|
|
346
|
+
if (old_field.name_space === new_field.name_space) {
|
|
383
347
|
appsConnectingMap.fields.push({
|
|
384
348
|
name_space: old_field.name_space,
|
|
385
349
|
old_field_id: old_field.field_id,
|
|
386
350
|
new_field_id: new_field.field_id
|
|
387
|
-
})
|
|
351
|
+
});
|
|
388
352
|
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
391
355
|
|
|
392
|
-
base_app.views_list
|
|
393
|
-
new_app.views_list
|
|
394
|
-
if (old_view.name
|
|
356
|
+
for (const old_view of base_app.views_list) {
|
|
357
|
+
for (const new_view of new_app.views_list) {
|
|
358
|
+
if (old_view.name === new_view.name) {
|
|
395
359
|
appsConnectingMap.views.push({
|
|
396
360
|
name: old_view.name,
|
|
397
361
|
old_view_id: old_view.view_id,
|
|
398
362
|
new_view_id: new_view.view_id
|
|
399
|
-
})
|
|
363
|
+
});
|
|
400
364
|
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
403
367
|
}
|
|
404
368
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
369
|
+
// Generic recursive walker to traverse deeply nested objects
|
|
370
|
+
_crawling(object, action) {
|
|
371
|
+
if (object === null) return;
|
|
408
372
|
|
|
409
|
-
|
|
410
|
-
properties.forEach(prop => {
|
|
411
|
-
let propertyValue = object[prop];
|
|
373
|
+
const properties = Object.keys(object);
|
|
412
374
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
375
|
+
for (const prop of properties) {
|
|
376
|
+
const propertyValue = object[prop];
|
|
377
|
+
|
|
378
|
+
if (typeof propertyValue !== 'undefined' && typeof object !== 'string') {
|
|
379
|
+
action(prop, propertyValue, object);
|
|
380
|
+
this._crawling(propertyValue, action);
|
|
381
|
+
}
|
|
418
382
|
}
|
|
419
383
|
}
|
|
420
384
|
|
|
421
|
-
|
|
385
|
+
// Resets all view-related IDs to avoid collisions when cloning
|
|
386
|
+
_resetViewsIds(app) {
|
|
422
387
|
app.view_init = 0;
|
|
423
388
|
|
|
424
|
-
this.
|
|
425
|
-
if (prop
|
|
389
|
+
this._crawling(app.views_list, (prop, value, parent) => {
|
|
390
|
+
if (prop === 'view_id' || prop === 'container_id') {
|
|
426
391
|
parent[prop] = 0;
|
|
427
392
|
}
|
|
428
|
-
})
|
|
393
|
+
});
|
|
429
394
|
}
|
|
430
395
|
|
|
431
|
-
|
|
432
|
-
|
|
396
|
+
// Reconnects all internal references (fields, apps, views, triggers) after cloning
|
|
397
|
+
_connectApps(apps, appsConnectingMap, source_apps) {
|
|
398
|
+
const hasProp = (prop, propArray) => propArray.some(key => prop.indexOf(key) !== -1);
|
|
433
399
|
|
|
434
|
-
|
|
435
|
-
appsConnectingMap.apps
|
|
400
|
+
for (const app of apps) {
|
|
401
|
+
for (const appMap of appsConnectingMap.apps) {
|
|
436
402
|
if (app.view_init === appMap.new_view_init) {
|
|
437
|
-
appsConnectingMap.views
|
|
403
|
+
for (const view of appsConnectingMap.views) {
|
|
438
404
|
if (appMap.old_view_init === view.old_view_id) {
|
|
439
405
|
app.view_init = view.new_view_id;
|
|
440
406
|
}
|
|
441
|
-
}
|
|
407
|
+
}
|
|
442
408
|
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
self.crawling(app.field_list, function (prop, value, parent) {
|
|
446
|
-
const fieldProps = ["field_id", "FieldId", "destination_field", "element_id"];
|
|
447
|
-
const appProps = ["app_id", "AppId", "destination_app"];
|
|
448
|
-
const viewProps = ["view_id"];
|
|
449
|
-
const srcProps = ["src"];
|
|
409
|
+
}
|
|
450
410
|
|
|
451
|
-
|
|
411
|
+
this._crawling(app.field_list, (prop, value, parent) => {
|
|
412
|
+
const fieldProps = ['field_id', 'FieldId', 'destination_field', 'element_id'];
|
|
413
|
+
const appProps = ['app_id', 'AppId', 'destination_app'];
|
|
414
|
+
const viewProps = ['view_id'];
|
|
415
|
+
const srcProps = ['src'];
|
|
452
416
|
|
|
453
417
|
if (hasProp(prop, fieldProps)) {
|
|
454
|
-
|
|
418
|
+
const fieldsArr = String(value).split(',');
|
|
419
|
+
const newFieldsArr = [];
|
|
455
420
|
|
|
456
|
-
appsConnectingMap.fields
|
|
457
|
-
|
|
421
|
+
for (const field of appsConnectingMap.fields) {
|
|
422
|
+
for (const fieldFromValue of fieldsArr) {
|
|
458
423
|
if (fieldFromValue == field.old_field_id) {
|
|
459
424
|
newFieldsArr.push(field.new_field_id);
|
|
460
425
|
}
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
if (newFieldsArr.length) {
|
|
465
|
-
parent[prop] = newFieldsArr.join(',');
|
|
426
|
+
}
|
|
466
427
|
}
|
|
428
|
+
|
|
429
|
+
if (newFieldsArr.length) parent[prop] = newFieldsArr.join(',');
|
|
467
430
|
}
|
|
468
431
|
|
|
469
432
|
if (hasProp(prop, appProps)) {
|
|
470
|
-
appsConnectingMap.apps
|
|
471
|
-
if (value ==
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
})
|
|
433
|
+
for (const appMap of appsConnectingMap.apps) {
|
|
434
|
+
if (value == appMap.old_app_id) parent[prop] = appMap.new_app_id;
|
|
435
|
+
}
|
|
475
436
|
}
|
|
476
437
|
|
|
477
438
|
if (hasProp(prop, viewProps)) {
|
|
478
|
-
appsConnectingMap.views
|
|
479
|
-
if (value == view.old_view_id)
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
})
|
|
439
|
+
for (const view of appsConnectingMap.views) {
|
|
440
|
+
if (value == view.old_view_id) parent[prop] = view.new_view_id;
|
|
441
|
+
}
|
|
483
442
|
}
|
|
484
443
|
|
|
485
444
|
if (hasProp(prop, srcProps) && isFinite(value)) {
|
|
486
445
|
const pron_name = 'container_id';
|
|
487
446
|
const old_app_id = appsConnectingMap.apps.find(appMap => appMap.new_app_id === app.app_id).old_app_id;
|
|
488
|
-
const path =
|
|
489
|
-
|
|
490
|
-
parent[prop] = `${self.getValueByPath(app.views_list, path, pron_name)}`;
|
|
447
|
+
const path = this._findPath(source_apps[old_app_id].views_list, pron_name, value);
|
|
448
|
+
parent[prop] = `${this._getValueByPath(app.views_list, path, pron_name)}`;
|
|
491
449
|
}
|
|
492
450
|
|
|
493
|
-
|
|
451
|
+
// Trigger nodes keep field IDs as object keys, so we need to remap both keys and connections.
|
|
452
|
+
if (prop.indexOf('trigger') !== -1 && value.model?.nodes) {
|
|
494
453
|
const nodes = value.model.nodes;
|
|
495
|
-
const nodeProperties = ["inputs", "outputs"];
|
|
496
454
|
|
|
497
455
|
for (const index in nodes) {
|
|
498
|
-
|
|
456
|
+
for (const property of ['inputs', 'outputs']) {
|
|
499
457
|
const node = nodes[index];
|
|
500
|
-
const numericProperties = Object.keys(node[property]).filter(
|
|
501
|
-
|
|
502
|
-
numericProperties.forEach((old_field_id) => {
|
|
503
|
-
const correspondFieldOfConnectingMap = appsConnectingMap.fields.filter((field) => field.old_field_id == old_field_id);
|
|
458
|
+
const numericProperties = Object.keys(node[property]).filter(p => p !== '' && !isNaN(p));
|
|
504
459
|
|
|
505
|
-
|
|
506
|
-
|
|
460
|
+
for (const old_field_id of numericProperties) {
|
|
461
|
+
const match = appsConnectingMap.fields.find(f => f.old_field_id == old_field_id);
|
|
507
462
|
|
|
463
|
+
if (match?.new_field_id) {
|
|
464
|
+
const new_field_id = match.new_field_id.toString();
|
|
508
465
|
const connectionObjectCopy = node[property][old_field_id];
|
|
509
|
-
connectionObjectCopy.connections.forEach((connection) => {
|
|
510
|
-
if (connection.input == old_field_id) {
|
|
511
|
-
connection.input = new_field_id;
|
|
512
|
-
}
|
|
513
|
-
if (connection.output == old_field_id) {
|
|
514
|
-
connection.output = new_field_id;
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
466
|
|
|
518
|
-
|
|
467
|
+
for (const connection of connectionObjectCopy.connections) {
|
|
468
|
+
if (connection.input == old_field_id) connection.input = new_field_id;
|
|
469
|
+
if (connection.output == old_field_id) connection.output = new_field_id;
|
|
470
|
+
}
|
|
519
471
|
|
|
472
|
+
delete node[property][old_field_id];
|
|
520
473
|
node[property][new_field_id] = connectionObjectCopy;
|
|
521
474
|
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
525
478
|
}
|
|
526
|
-
})
|
|
527
|
-
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
528
481
|
|
|
529
482
|
return apps;
|
|
530
483
|
}
|
|
531
484
|
|
|
532
|
-
|
|
485
|
+
// Utility to safely access nested value by path
|
|
486
|
+
_getValueByPath(source, path, prop) {
|
|
533
487
|
let temp = source;
|
|
534
|
-
|
|
535
|
-
path.split('.').forEach(item => {
|
|
488
|
+
for (const item of path.split('.')) {
|
|
536
489
|
temp = temp[item];
|
|
537
|
-
}
|
|
538
|
-
|
|
490
|
+
}
|
|
539
491
|
return temp[prop];
|
|
540
492
|
}
|
|
541
493
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
494
|
+
// Finds path to a specific value inside deeply nested structure
|
|
495
|
+
_findPath(source, prop, value) {
|
|
545
496
|
let isFound = false;
|
|
546
497
|
let toClearPath = false;
|
|
547
498
|
let path = [];
|
|
548
499
|
|
|
549
|
-
|
|
500
|
+
this._crawling(source, (propSource, valueSource, parentSource) => {
|
|
550
501
|
if (toClearPath) {
|
|
551
502
|
path.reverse().forEach(item => {
|
|
552
503
|
if (toClearPath) {
|
|
553
504
|
item.delete = true;
|
|
554
|
-
|
|
555
505
|
if (item.parent === parentSource) {
|
|
556
506
|
toClearPath = false;
|
|
557
507
|
path = path.filter(item => !item.delete);
|
|
558
508
|
path.reverse();
|
|
559
509
|
}
|
|
560
510
|
}
|
|
561
|
-
})
|
|
511
|
+
});
|
|
562
512
|
}
|
|
563
513
|
|
|
564
514
|
if (!isFound) {
|
|
565
515
|
if (typeof parentSource[propSource] === 'object') {
|
|
566
|
-
path.push({
|
|
567
|
-
prop: propSource,
|
|
568
|
-
parent: parentSource
|
|
569
|
-
});
|
|
516
|
+
path.push({ prop: propSource, parent: parentSource });
|
|
570
517
|
}
|
|
571
518
|
|
|
572
519
|
if (valueSource == value && prop === propSource) {
|
|
@@ -580,24 +527,23 @@ export default class AppsTemplateService {
|
|
|
580
527
|
return path.map(item => item.prop).join('.');
|
|
581
528
|
}
|
|
582
529
|
|
|
583
|
-
|
|
584
|
-
|
|
530
|
+
// Prepares clean app template by stripping IDs and resetting system fields
|
|
531
|
+
_prepareAppTemplate(appToTemplate) {
|
|
532
|
+
const app = JSON.parse(JSON.stringify(appToTemplate));
|
|
585
533
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
self.crawling(app.views_list, function (prop, value, parent) {
|
|
589
|
-
if (prop == "element_id") {
|
|
534
|
+
this._crawling(app.views_list, (prop, value, parent) => {
|
|
535
|
+
if (prop === 'element_id') {
|
|
590
536
|
parent.element_id = -Number(value);
|
|
591
537
|
parent.create_element = -Number(value);
|
|
592
538
|
}
|
|
593
|
-
})
|
|
539
|
+
});
|
|
594
540
|
|
|
595
|
-
app.field_list
|
|
541
|
+
for (const field of app.field_list) {
|
|
596
542
|
field.create_field = -Number(field.field_id);
|
|
597
543
|
field.element_id = -Number(field.field_id);
|
|
598
544
|
field.field_id = -Number(field.field_id);
|
|
599
|
-
field.field_value =
|
|
600
|
-
}
|
|
545
|
+
field.field_value = '';
|
|
546
|
+
}
|
|
601
547
|
|
|
602
548
|
app.items_list = [];
|
|
603
549
|
|
|
@@ -609,6 +555,5 @@ export default class AppsTemplateService {
|
|
|
609
555
|
delete app.file_list;
|
|
610
556
|
|
|
611
557
|
return app;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
}
|
|
558
|
+
}
|
|
559
|
+
}
|