retold-data-service 2.0.21 → 2.0.23
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/.quackage-comprehension-loader.json +19 -0
- package/bin/retold-data-service-clone.js +4 -1
- package/generate-bookstore-comprehension.js +645 -0
- package/package.json +7 -7
- package/source/Retold-Data-Service.js +30 -2
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Load.js +345 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Schema.js +97 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Session.js +221 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-WebUI.js +57 -0
- package/source/services/comprehension-loader/Retold-Data-Service-ComprehensionLoader.js +536 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader-Configuration.json +9 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +86 -0
- package/source/services/comprehension-loader/pict-app/Pict-ComprehensionLoader-Bundle.js +6 -0
- package/source/services/comprehension-loader/pict-app/providers/Pict-Provider-ComprehensionLoader.js +760 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +360 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +472 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Schema.js +119 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Session.js +269 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Source.js +330 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js +6794 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -0
- package/source/services/comprehension-loader/web/comprehension-loader.min.js +2 -0
- package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -0
- package/source/services/comprehension-loader/web/index.html +17 -0
- package/source/services/data-cloner/DataCloner-Command-Schema.js +407 -15
- package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +59 -1
- package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +1 -0
- package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +125 -5
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +18 -8
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Deploy.js +104 -1
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +1 -1
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +12 -0
- package/source/services/data-cloner/web/data-cloner.js +201 -139
- package/source/services/data-cloner/web/data-cloner.js.map +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
- package/test/RetoldDataService_tests.js +225 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComprehensionLoader Session Management Routes
|
|
3
|
+
*
|
|
4
|
+
* Registers /comprehension_load/session/* endpoints for remote session
|
|
5
|
+
* configuration, authentication, check, and deauthentication via
|
|
6
|
+
* pict-sessionmanager.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} pComprehensionLoaderService - The RetoldDataServiceComprehensionLoader instance
|
|
9
|
+
* @param {Object} pOratorServiceServer - The Orator ServiceServer instance
|
|
10
|
+
*/
|
|
11
|
+
module.exports = (pComprehensionLoaderService, pOratorServiceServer) =>
|
|
12
|
+
{
|
|
13
|
+
let tmpFable = pComprehensionLoaderService.fable;
|
|
14
|
+
let tmpLoadState = pComprehensionLoaderService.loadState;
|
|
15
|
+
let tmpPict = pComprehensionLoaderService.pict;
|
|
16
|
+
let tmpPrefix = pComprehensionLoaderService.routePrefix;
|
|
17
|
+
|
|
18
|
+
// POST /comprehension_load/session/configure
|
|
19
|
+
pOratorServiceServer.post(`${tmpPrefix}/session/configure`,
|
|
20
|
+
(pRequest, pResponse, fNext) =>
|
|
21
|
+
{
|
|
22
|
+
let tmpBody = pRequest.body || {};
|
|
23
|
+
let tmpServerURL = tmpBody.ServerURL;
|
|
24
|
+
|
|
25
|
+
if (!tmpServerURL)
|
|
26
|
+
{
|
|
27
|
+
pResponse.send(400, { Success: false, Error: 'ServerURL is required.' });
|
|
28
|
+
return fNext();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
tmpLoadState.RemoteServerURL = tmpServerURL;
|
|
32
|
+
|
|
33
|
+
// Remove existing session if reconfiguring
|
|
34
|
+
if (tmpPict.SessionManager.getSession('Remote'))
|
|
35
|
+
{
|
|
36
|
+
tmpPict.SessionManager.deauthenticate('Remote');
|
|
37
|
+
delete tmpPict.SessionManager.sessions['Remote'];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Extract domain from ServerURL for cookie matching
|
|
41
|
+
let tmpDomainMatch = tmpBody.DomainMatch;
|
|
42
|
+
if (!tmpDomainMatch)
|
|
43
|
+
{
|
|
44
|
+
try
|
|
45
|
+
{
|
|
46
|
+
let tmpURL = new URL(tmpServerURL);
|
|
47
|
+
tmpDomainMatch = tmpURL.hostname;
|
|
48
|
+
}
|
|
49
|
+
catch (pParseError)
|
|
50
|
+
{
|
|
51
|
+
tmpDomainMatch = tmpServerURL;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Helper: ensure a URI template is fully qualified with the server URL
|
|
56
|
+
let fQualifyURI = (pTemplate, pDefault) =>
|
|
57
|
+
{
|
|
58
|
+
let tmpTemplate = pTemplate || pDefault;
|
|
59
|
+
if (tmpTemplate.indexOf('://') > -1)
|
|
60
|
+
{
|
|
61
|
+
return tmpTemplate;
|
|
62
|
+
}
|
|
63
|
+
let tmpPath = tmpTemplate.replace(/^\//, '');
|
|
64
|
+
let tmpVersionMatch = tmpServerURL.match(/\/(\d+\.\d+)\/?$/);
|
|
65
|
+
if (tmpVersionMatch)
|
|
66
|
+
{
|
|
67
|
+
let tmpURLPrefix = tmpVersionMatch[1] + '/';
|
|
68
|
+
if (tmpPath.indexOf(tmpURLPrefix) === 0)
|
|
69
|
+
{
|
|
70
|
+
tmpPath = tmpPath.substring(tmpURLPrefix.length);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return tmpServerURL + tmpPath;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let tmpSessionConfig = (
|
|
77
|
+
{
|
|
78
|
+
Type: 'Cookie',
|
|
79
|
+
|
|
80
|
+
AuthenticationMethod: tmpBody.AuthenticationMethod || 'get',
|
|
81
|
+
AuthenticationURITemplate: fQualifyURI(tmpBody.AuthenticationURITemplate, 'Authenticate/{~D:Record.UserName~}/{~D:Record.Password~}'),
|
|
82
|
+
AuthenticationRetryCount: 2,
|
|
83
|
+
AuthenticationRetryDebounce: 200,
|
|
84
|
+
|
|
85
|
+
CheckSessionURITemplate: fQualifyURI(tmpBody.CheckSessionURITemplate, 'CheckSession'),
|
|
86
|
+
CheckSessionLoginMarkerType: tmpBody.CheckSessionLoginMarkerType || 'boolean',
|
|
87
|
+
CheckSessionLoginMarker: tmpBody.CheckSessionLoginMarker || 'LoggedIn',
|
|
88
|
+
|
|
89
|
+
DomainMatch: tmpDomainMatch,
|
|
90
|
+
CookieName: tmpBody.CookieName || 'SessionID',
|
|
91
|
+
CookieValueAddress: tmpBody.CookieValueAddress || 'SessionID',
|
|
92
|
+
CookieValueTemplate: tmpBody.CookieValueTemplate || false
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (tmpBody.AuthenticationMethod === 'post')
|
|
96
|
+
{
|
|
97
|
+
tmpSessionConfig.AuthenticationRequestBody = tmpBody.AuthenticationRequestBody || (
|
|
98
|
+
{
|
|
99
|
+
username: '{~D:Record.UserName~}',
|
|
100
|
+
password: '{~D:Record.Password~}'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
tmpPict.SessionManager.addSession('Remote', tmpSessionConfig);
|
|
105
|
+
tmpPict.SessionManager.connectToRestClient();
|
|
106
|
+
|
|
107
|
+
tmpLoadState.SessionConfigured = true;
|
|
108
|
+
tmpLoadState.SessionAuthenticated = false;
|
|
109
|
+
|
|
110
|
+
tmpFable.log.info(`Comprehension Loader: Session configured for ${tmpServerURL} (domain: ${tmpDomainMatch})`);
|
|
111
|
+
|
|
112
|
+
pResponse.send(200,
|
|
113
|
+
{
|
|
114
|
+
Success: true,
|
|
115
|
+
ServerURL: tmpServerURL,
|
|
116
|
+
DomainMatch: tmpDomainMatch,
|
|
117
|
+
AuthenticationMethod: tmpSessionConfig.AuthenticationMethod
|
|
118
|
+
});
|
|
119
|
+
return fNext();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// POST /comprehension_load/session/authenticate
|
|
123
|
+
pOratorServiceServer.post(`${tmpPrefix}/session/authenticate`,
|
|
124
|
+
(pRequest, pResponse, fNext) =>
|
|
125
|
+
{
|
|
126
|
+
if (!tmpLoadState.SessionConfigured)
|
|
127
|
+
{
|
|
128
|
+
pResponse.send(400, { Success: false, Error: 'Session not configured. Call POST /comprehension_load/session/configure first.' });
|
|
129
|
+
return fNext();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let tmpBody = pRequest.body || {};
|
|
133
|
+
let tmpCredentials = (
|
|
134
|
+
{
|
|
135
|
+
UserName: tmpBody.UserName || tmpBody.username,
|
|
136
|
+
Password: tmpBody.Password || tmpBody.password
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (!tmpCredentials.UserName || !tmpCredentials.Password)
|
|
140
|
+
{
|
|
141
|
+
pResponse.send(400, { Success: false, Error: 'UserName and Password are required.' });
|
|
142
|
+
return fNext();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
tmpFable.log.info(`Comprehension Loader: Authenticating as ${tmpCredentials.UserName}...`);
|
|
146
|
+
|
|
147
|
+
tmpPict.SessionManager.authenticate('Remote', tmpCredentials,
|
|
148
|
+
(pAuthError, pSessionState) =>
|
|
149
|
+
{
|
|
150
|
+
if (pAuthError)
|
|
151
|
+
{
|
|
152
|
+
tmpFable.log.error(`Comprehension Loader: Authentication failed: ${pAuthError.message || pAuthError}`);
|
|
153
|
+
tmpLoadState.SessionAuthenticated = false;
|
|
154
|
+
pResponse.send(401,
|
|
155
|
+
{
|
|
156
|
+
Success: false,
|
|
157
|
+
Error: `Authentication failed: ${pAuthError.message || pAuthError}`
|
|
158
|
+
});
|
|
159
|
+
return fNext();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
tmpLoadState.SessionAuthenticated = pSessionState && pSessionState.Authenticated;
|
|
163
|
+
|
|
164
|
+
tmpFable.log.info(`Comprehension Loader: Authentication ${tmpLoadState.SessionAuthenticated ? 'succeeded' : 'failed'}.`);
|
|
165
|
+
|
|
166
|
+
pResponse.send(200,
|
|
167
|
+
{
|
|
168
|
+
Success: tmpLoadState.SessionAuthenticated,
|
|
169
|
+
Authenticated: tmpLoadState.SessionAuthenticated,
|
|
170
|
+
SessionData: pSessionState ? pSessionState.SessionData : {}
|
|
171
|
+
});
|
|
172
|
+
return fNext();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// GET /comprehension_load/session/check
|
|
177
|
+
pOratorServiceServer.get(`${tmpPrefix}/session/check`,
|
|
178
|
+
(pRequest, pResponse, fNext) =>
|
|
179
|
+
{
|
|
180
|
+
if (!tmpLoadState.SessionConfigured)
|
|
181
|
+
{
|
|
182
|
+
pResponse.send(200,
|
|
183
|
+
{
|
|
184
|
+
Configured: false,
|
|
185
|
+
Authenticated: false
|
|
186
|
+
});
|
|
187
|
+
return fNext();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
tmpPict.SessionManager.checkSession('Remote',
|
|
191
|
+
(pCheckError, pAuthenticated, pCheckData) =>
|
|
192
|
+
{
|
|
193
|
+
tmpLoadState.SessionAuthenticated = pAuthenticated;
|
|
194
|
+
|
|
195
|
+
pResponse.send(200,
|
|
196
|
+
{
|
|
197
|
+
Configured: tmpLoadState.SessionConfigured,
|
|
198
|
+
Authenticated: pAuthenticated,
|
|
199
|
+
ServerURL: tmpLoadState.RemoteServerURL,
|
|
200
|
+
CheckData: pCheckData || {}
|
|
201
|
+
});
|
|
202
|
+
return fNext();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// POST /comprehension_load/session/deauthenticate
|
|
207
|
+
pOratorServiceServer.post(`${tmpPrefix}/session/deauthenticate`,
|
|
208
|
+
(pRequest, pResponse, fNext) =>
|
|
209
|
+
{
|
|
210
|
+
if (tmpLoadState.SessionConfigured)
|
|
211
|
+
{
|
|
212
|
+
tmpPict.SessionManager.deauthenticate('Remote');
|
|
213
|
+
}
|
|
214
|
+
tmpLoadState.SessionAuthenticated = false;
|
|
215
|
+
|
|
216
|
+
tmpFable.log.info('Comprehension Loader: Session deauthenticated.');
|
|
217
|
+
|
|
218
|
+
pResponse.send(200, { Success: true, Authenticated: false });
|
|
219
|
+
return fNext();
|
|
220
|
+
});
|
|
221
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComprehensionLoader Web UI Routes
|
|
3
|
+
*
|
|
4
|
+
* Serves the Pict-application-based comprehension loader web UI.
|
|
5
|
+
* The HTML entry point loads pict.min.js (from node_modules) and the
|
|
6
|
+
* Quackage-built application bundle.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} pComprehensionLoaderService - The RetoldDataServiceComprehensionLoader instance
|
|
9
|
+
* @param {Object} pOratorServiceServer - The Orator ServiceServer instance
|
|
10
|
+
*/
|
|
11
|
+
module.exports = (pComprehensionLoaderService, pOratorServiceServer) =>
|
|
12
|
+
{
|
|
13
|
+
let libFs = require('fs');
|
|
14
|
+
let libPath = require('path');
|
|
15
|
+
|
|
16
|
+
let tmpPrefix = pComprehensionLoaderService.routePrefix;
|
|
17
|
+
let tmpWebDir = libPath.join(__dirname, 'web');
|
|
18
|
+
|
|
19
|
+
// Helper: serve a static file with the given content type
|
|
20
|
+
let fServeFile = (pFilePath, pContentType) =>
|
|
21
|
+
{
|
|
22
|
+
return (pRequest, pResponse, fNext) =>
|
|
23
|
+
{
|
|
24
|
+
try
|
|
25
|
+
{
|
|
26
|
+
let tmpContent = libFs.readFileSync(pFilePath, 'utf8');
|
|
27
|
+
pResponse.writeHead(200, { 'Content-Type': pContentType + '; charset=utf-8' });
|
|
28
|
+
pResponse.write(tmpContent);
|
|
29
|
+
pResponse.end();
|
|
30
|
+
}
|
|
31
|
+
catch (pReadError)
|
|
32
|
+
{
|
|
33
|
+
pResponse.send(500, { Success: false, Error: 'Failed to load ' + libPath.basename(pFilePath) });
|
|
34
|
+
}
|
|
35
|
+
return fNext();
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ---- HTML ----
|
|
40
|
+
pOratorServiceServer.get(`${tmpPrefix}/`,
|
|
41
|
+
fServeFile(libPath.join(tmpWebDir, 'index.html'), 'text/html'));
|
|
42
|
+
|
|
43
|
+
// Redirect /comprehension_load -> /comprehension_load/
|
|
44
|
+
pOratorServiceServer.get(`${tmpPrefix}`,
|
|
45
|
+
(pRequest, pResponse, fNext) =>
|
|
46
|
+
{
|
|
47
|
+
pResponse.redirect(`${tmpPrefix}/`, fNext);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ---- Pict library (from node_modules) ----
|
|
51
|
+
pOratorServiceServer.get(`${tmpPrefix}/pict.min.js`,
|
|
52
|
+
fServeFile(require.resolve('pict/dist/pict.min.js'), 'application/javascript'));
|
|
53
|
+
|
|
54
|
+
// ---- Application bundle ----
|
|
55
|
+
pOratorServiceServer.get(`${tmpPrefix}/comprehension-loader.js`,
|
|
56
|
+
fServeFile(libPath.join(tmpWebDir, 'comprehension-loader.js'), 'application/javascript'));
|
|
57
|
+
};
|