retold-data-service 1.0.4 → 2.0.0
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/.config/configstore/update-notifier-npm.json +1 -7
- package/README.md +11 -21
- package/debug/Harness.js +33 -46
- package/debug/bookstore-configuration.json +2 -1
- package/package.json +5 -5
- package/source/Retold-Data-Service.js +34 -106
- package/test/RetoldDataService_tests.js +3 -6
- package/test/model/fable-configuration.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,27 @@
|
|
|
1
1
|
# Retold Data Service
|
|
2
2
|
|
|
3
|
-
Provide a consistent back or mid-tier data service
|
|
4
|
-
abstracted behind configuration. Leaving a simple mechanism to add complexity
|
|
5
|
-
where desired.
|
|
3
|
+
Provide a consistent back or mid-tier data service.
|
|
6
4
|
|
|
7
5
|
## Basic Usage
|
|
8
6
|
|
|
9
|
-
This library
|
|
7
|
+
This library can run in any of three types of configurations:
|
|
10
8
|
|
|
11
9
|
* Configuration Driven
|
|
12
10
|
* Managed via Backplane Endpoints
|
|
13
11
|
* Hybrid (configuration plus endpoint)
|
|
14
12
|
|
|
15
|
-
Currently only configuration driven mode is implemented.
|
|
16
|
-
|
|
17
13
|
## Configuration-Driven Mode
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
for the schemas.
|
|
21
|
-
|
|
22
|
-
Default configuration:
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
{
|
|
26
|
-
FullMeadowSchemaPath: `${process.cwd()}/model/`,
|
|
27
|
-
FullMeadowSchemaFilename: `Model-Extended.json`,
|
|
15
|
+
The service looks for a `Retold` stanza in the configuration. For instance:
|
|
28
16
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
```
|
|
18
|
+
{
|
|
19
|
+
...
|
|
20
|
+
"Retold": {
|
|
21
|
+
"MeadowModel": "./meadow-schema-extended.json"
|
|
22
|
+
},
|
|
23
|
+
...
|
|
24
|
+
}
|
|
35
25
|
```
|
|
36
26
|
|
|
37
27
|
The three auto-configured parameters are:
|
package/debug/Harness.js
CHANGED
|
@@ -3,51 +3,38 @@ const _Settings = require('./bookstore-configuration.json');
|
|
|
3
3
|
const libFable = require('fable');
|
|
4
4
|
|
|
5
5
|
_Fable = new libFable(_Settings);
|
|
6
|
-
let libRetoldDataService = _Fable.serviceManager.addAndInstantiateServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
(fAfterInitializeComplete) =>
|
|
10
|
-
{
|
|
11
|
-
// Create a post operation behavior for the book Read singular record endpoint only
|
|
12
|
-
_Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior('Read-PostOperation',
|
|
13
|
-
(pRequest, pRequestState, fRequestComplete) =>
|
|
14
|
-
{
|
|
15
|
-
// Get the join records
|
|
16
|
-
_Fable.DAL.BookAuthorJoin.doReads(_Fable.DAL.BookAuthorJoin.query.addFilter('IDBook', pRequestState.Record.IDBook),
|
|
17
|
-
(pJoinReadError, pJoinReadQuery, pJoinRecords)=>
|
|
18
|
-
{
|
|
19
|
-
let tmpAuthorList = [];
|
|
20
|
-
for (let j = 0; j < pJoinRecords.length; j++)
|
|
21
|
-
{
|
|
22
|
-
tmpAuthorList.push(pJoinRecords[j].IDAuthor);
|
|
23
|
-
}
|
|
24
|
-
if (tmpAuthorList.length < 1)
|
|
25
|
-
{
|
|
26
|
-
_Fable.log.trace(`Found no authors for IDBook ${pRequestState.Record.IDBook} (${pRequestState.Record.Title}). What even is a book without authors?`)
|
|
27
|
-
pRequestState.Record.Authors = [];
|
|
28
|
-
return fRequestComplete();
|
|
29
|
-
}
|
|
30
|
-
else
|
|
31
|
-
{
|
|
32
|
-
_Fable.DAL.Author.doReads(_Fable.DAL.Author.query.addFilter('IDAuthor', tmpAuthorList, 'IN'),
|
|
33
|
-
(pReadsError, pReadsQuery, pAuthors)=>
|
|
34
|
-
{
|
|
35
|
-
pRequestState.Record.Authors = pAuthors;
|
|
36
|
-
_Fable.log.info(`Found ${pAuthors.length} authors for IDBook ${pRequestState.Record.IDBook} (${pRequestState.Record.Title}).`)
|
|
37
|
-
return fRequestComplete();
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
7
|
+
_Fable.serviceManager.addAndInstantiateServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
|
|
42
8
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
9
|
+
_Fable.RetoldDataService.initializeService(
|
|
10
|
+
(pError) =>
|
|
11
|
+
{
|
|
12
|
+
_Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior('Read-PostOperation',
|
|
13
|
+
(pRequest, pRequestState, fComplete) =>
|
|
14
|
+
{
|
|
15
|
+
// Get the join records
|
|
16
|
+
_Fable.DAL.BookAuthorJoin.doReads(_Fable.DAL.BookAuthorJoin.query.addFilter('IDBook', pRequestState.Record.IDBook),
|
|
17
|
+
(pJoinReadError, pJoinReadQuery, pJoinRecords)=>
|
|
18
|
+
{
|
|
19
|
+
let tmpAuthorList = [];
|
|
20
|
+
for (let j = 0; j < pJoinRecords.length; j++)
|
|
21
|
+
{
|
|
22
|
+
tmpAuthorList.push(pJoinRecords[j].IDAuthor);
|
|
23
|
+
}
|
|
24
|
+
if (tmpAuthorList.length < 1)
|
|
25
|
+
{
|
|
26
|
+
pRequestState.Record.Authors = [];
|
|
27
|
+
return fComplete();
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
_Fable.DAL.Author.doReads(_Fable.DAL.Author.query.addFilter('IDAuthor', tmpAuthorList, 'IN'),
|
|
32
|
+
(pReadsError, pReadsQuery, pAuthors)=>
|
|
33
|
+
{
|
|
34
|
+
pRequestState.Record.Authors = pAuthors;
|
|
35
|
+
return fComplete();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "retold-data-service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Serve up a whole model!",
|
|
5
5
|
"main": "source/Retold-Data-Service.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"test": "./node_modules/.bin/mocha -u tdd -R spec",
|
|
10
10
|
"build": "npx quack build",
|
|
11
11
|
"docker-dev-build": "docker build ./ -f Dockerfile_LUXURYCode -t retold-data-service-image:local",
|
|
12
|
-
"docker-dev-run": "docker run -it -d --name retold-data-service-dev -p 44444:8080 -p 43306:3306 -p
|
|
12
|
+
"docker-dev-run": "docker run -it -d --name retold-data-service-dev -p 44444:8080 -p 43306:3306 -p 48086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/retold-data-service\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold-data-service-image:local",
|
|
13
13
|
"docker-dev-shell": "docker exec -it retold-data-service-dev /bin/bash"
|
|
14
14
|
},
|
|
15
15
|
"mocha": {
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://github.com/stevenvelozo/retold-data-service",
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"quackage": "^1.0.
|
|
49
|
+
"quackage": "^1.0.28"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"fable": "^3.0.
|
|
52
|
+
"fable": "^3.0.112",
|
|
53
53
|
"fable-serviceproviderbase": "^3.0.12",
|
|
54
54
|
"meadow": "^2.0.15",
|
|
55
55
|
"meadow-connection-mysql": "^1.0.4",
|
|
56
|
-
"meadow-endpoints": "^4.0.
|
|
56
|
+
"meadow-endpoints": "^4.0.5",
|
|
57
57
|
"orator": "^4.0.2",
|
|
58
58
|
"orator-serviceserver-restify": "^2.0.2"
|
|
59
59
|
}
|
|
@@ -9,6 +9,7 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
|
9
9
|
|
|
10
10
|
const libOrator = require('orator');
|
|
11
11
|
const libOratorServiceServerRestify = require('orator-serviceserver-restify');
|
|
12
|
+
|
|
12
13
|
const libMeadow = require('meadow');
|
|
13
14
|
const libMeadowEndpoints = require('meadow-endpoints');
|
|
14
15
|
|
|
@@ -21,6 +22,7 @@ const defaultDataServiceSettings = (
|
|
|
21
22
|
DALMeadowSchemaPrefix: `Model-MeadowSchema-`,
|
|
22
23
|
DALMeadowSchemaPostfix: ``,
|
|
23
24
|
|
|
25
|
+
AutoInitializeDataService: true,
|
|
24
26
|
AutoStartOrator: true
|
|
25
27
|
});
|
|
26
28
|
|
|
@@ -33,78 +35,48 @@ class RetoldDataService extends libFableServiceProviderBase
|
|
|
33
35
|
this.serviceType = 'RetoldDataService';
|
|
34
36
|
|
|
35
37
|
this.options = this.fable.Utility.extend(defaultDataServiceSettings, this.options);
|
|
36
|
-
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
// Add the restify server provider and orator base class to fable
|
|
40
|
+
this.fable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
|
|
41
|
+
this.fable.serviceManager.addServiceType('Orator', libOrator);
|
|
42
|
+
|
|
43
|
+
// Initialize Restify
|
|
44
|
+
this.fable.serviceManager.instantiateServiceProvider('OratorServiceServer', this.options);
|
|
45
|
+
|
|
46
|
+
// Initialize Orator, which will automatically use the default `OratorServiceServer` service we just instantiated
|
|
47
|
+
this.fable.serviceManager.instantiateServiceProvider('Orator', this.options);
|
|
48
|
+
|
|
49
|
+
// TODO: This code will be much cleaner with meadow and meadow-endpoints as services
|
|
50
|
+
this._Meadow = libMeadow.new(_Fable);
|
|
51
|
+
|
|
52
|
+
// Create DAL objects for each table in the schema
|
|
53
|
+
// These will be unnecessary when meadow and meadow-endpoints are full fledged fable services
|
|
54
|
+
this._DAL = {};
|
|
55
|
+
this.fable.DAL = this._DAL;
|
|
56
|
+
this._MeadowEndpoints = {};
|
|
57
|
+
this.fable.MeadowEndpoints = this._MeadowEndpoints;
|
|
46
58
|
}
|
|
47
59
|
|
|
48
|
-
|
|
60
|
+
initializeService(fCallback)
|
|
49
61
|
{
|
|
50
62
|
if (this.serviceInitialized)
|
|
51
63
|
{
|
|
52
64
|
this.fable.log.error("Retold Data Service Application is being initialized but has already been initialized...");
|
|
65
|
+
return fCallback(new Error("Retold Data Service Application is being initialized but has already been initialized..."));
|
|
53
66
|
}
|
|
54
67
|
else
|
|
55
68
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Phase II: Make sure there is an Orator server available so we can put the behaviors somewhere
|
|
60
|
-
tmpAnticipate.anticipate(
|
|
61
|
-
function (fCallback)
|
|
69
|
+
this.fable.log.info(`The Retold Data Service is Auto Starting Orator`);
|
|
70
|
+
this.fable.Orator.startWebServer(
|
|
71
|
+
(pError) =>
|
|
62
72
|
{
|
|
63
|
-
|
|
64
|
-
if (!this.fable.serviceClasses.hasOwnProperty('OratorServiceServer'))
|
|
65
|
-
{
|
|
66
|
-
this.fable.serviceManager.addAndInstantiateServiceType('OratorServiceServer', libOratorServiceServerRestify, this.fable.settings);
|
|
67
|
-
}
|
|
68
|
-
else if (!this.fable.OratorServiceServer)
|
|
69
|
-
{
|
|
70
|
-
this.fable.serviceManager.instantiateServiceProvider('OratorServiceServer', this.fable.settings);
|
|
71
|
-
}
|
|
72
|
-
return fCallback();
|
|
73
|
-
}.bind(this));
|
|
74
|
-
tmpAnticipate.anticipate(
|
|
75
|
-
function (fCallback)
|
|
76
|
-
{
|
|
77
|
-
// Check if Fable already has an Orator service provider, otherwise add one.
|
|
78
|
-
if (!this.fable.serviceClasses.hasOwnProperty('Orator'))
|
|
79
|
-
{
|
|
80
|
-
this.fable.serviceManager.addAndInstantiateServiceType('Orator', libOrator, this.fable.settings);
|
|
81
|
-
}
|
|
82
|
-
else if (!this.fable.Orator)
|
|
73
|
+
if (pError)
|
|
83
74
|
{
|
|
84
|
-
|
|
75
|
+
console.log(`Error auto-starting Orator: ${pError}`, pError);
|
|
76
|
+
return fCallback(pError);
|
|
85
77
|
}
|
|
86
|
-
return fCallback();
|
|
87
|
-
}.bind(this));
|
|
88
|
-
// Phase III: Initialize orator
|
|
89
|
-
tmpAnticipate.anticipate(this.fable.Orator.initialize.bind(this.fable.Orator));
|
|
90
|
-
// Phase IV: Initialize Meadow (this will simplify greatly when meadow and meadow-endpoints are service-ized)
|
|
91
|
-
tmpAnticipate.anticipate(
|
|
92
|
-
function (fCallback)
|
|
93
|
-
{
|
|
94
|
-
// TODO: This code will be much cleaner with meadow and meadow-endpoints as a service. They are the only toolchain that is left!
|
|
95
|
-
this._Meadow = libMeadow.new(_Fable);
|
|
96
78
|
|
|
97
|
-
//
|
|
98
|
-
// These will be unnecessary when meadow and meadow-endpoints are full fledged fable services and then
|
|
99
|
-
// refactored to auto-initialize on a schema provider.
|
|
100
|
-
// For now, this is a mock of how we expect that service to provide interfaces to DAL objects.
|
|
101
|
-
this._DAL = {};
|
|
102
|
-
this.fable.DAL = this._DAL;
|
|
103
|
-
// And a mock of how the endpoints will be expressed.
|
|
104
|
-
this._MeadowEndpoints = {};
|
|
105
|
-
this.fable.MeadowEndpoints = this._MeadowEndpoints;
|
|
106
|
-
|
|
107
|
-
// TODO: This goes away when the above use services in a sane fashion.
|
|
79
|
+
// TODO: Change this to an option (e.g. we might want to do ALASQL)
|
|
108
80
|
// Load the mysql connection for meadow if it doesn't exist yet
|
|
109
81
|
_Fable.serviceManager.addAndInstantiateServiceType('MeadowMySQLProvider', require('meadow-connection-mysql'));
|
|
110
82
|
|
|
@@ -113,15 +85,10 @@ class RetoldDataService extends libFableServiceProviderBase
|
|
|
113
85
|
|
|
114
86
|
this.serviceInitialized = false;
|
|
115
87
|
|
|
116
|
-
|
|
117
|
-
}.bind(this));
|
|
118
|
-
// Phase V: Initialize the meat of the matter (the retold data service)
|
|
119
|
-
tmpAnticipate.anticipate(
|
|
120
|
-
function(fCallback)
|
|
121
|
-
{
|
|
122
|
-
this.fable.log.info("Retold Data Service DAL methods initializing...");
|
|
88
|
+
this.fable.log.info("Retold Data Service Application is starting up...");
|
|
123
89
|
|
|
124
90
|
// Create DAL objects for each table in the schema
|
|
91
|
+
|
|
125
92
|
// 1. Load full compiled schema of the model from stricture
|
|
126
93
|
_Fable.log.info(`...loading full model stricture schema...`);
|
|
127
94
|
this.fullModel = require (`${this.options.FullMeadowSchemaPath}${this.options.FullMeadowSchemaFilename}`);
|
|
@@ -153,49 +120,10 @@ class RetoldDataService extends libFableServiceProviderBase
|
|
|
153
120
|
|
|
154
121
|
this.serviceInitialized = true;
|
|
155
122
|
|
|
156
|
-
return fCallback();
|
|
157
|
-
}
|
|
158
|
-
// Phase VI: Optionally auto start the orator server.
|
|
159
|
-
tmpAnticipate.anticipate(
|
|
160
|
-
function (fCallback)
|
|
161
|
-
{
|
|
162
|
-
if (this.options.AutoStartOrator)
|
|
163
|
-
{
|
|
164
|
-
this.fable.log.info(`The Retold Data Service is Auto Starting Orator...`);
|
|
165
|
-
this.fable.Orator.startWebServer(fCallback);
|
|
166
|
-
}
|
|
167
|
-
else
|
|
168
|
-
{
|
|
169
|
-
return fCallback();
|
|
170
|
-
}
|
|
171
|
-
}.bind(this));
|
|
172
|
-
tmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));
|
|
173
|
-
|
|
174
|
-
// Now anticipate (wait for) initialization to complete.
|
|
175
|
-
tmpAnticipate.wait(
|
|
176
|
-
function (pError)
|
|
177
|
-
{
|
|
178
|
-
if (pError)
|
|
179
|
-
{
|
|
180
|
-
let tmpErrorMessage = `Error initializing Retold Data Service: ${pError}`;
|
|
181
|
-
this.log.error(tmpErrorMessage);
|
|
182
|
-
// TODO: Should this bubble up? I think not.
|
|
183
|
-
throw new Error(tmpErrorMessage);
|
|
184
|
-
}
|
|
185
|
-
return fCallback();
|
|
186
|
-
}.bind(this));
|
|
123
|
+
return fCallback(pError);
|
|
124
|
+
});
|
|
187
125
|
}
|
|
188
126
|
}
|
|
189
|
-
|
|
190
|
-
onAfterInitializeAsync(fCallback)
|
|
191
|
-
{
|
|
192
|
-
this.onAfterInitialize();
|
|
193
|
-
return fCallback();
|
|
194
|
-
}
|
|
195
|
-
onAfterInitialize()
|
|
196
|
-
{
|
|
197
|
-
this.log.info("Retold Data Service Application has completed an initialization attempt.");
|
|
198
|
-
}
|
|
199
127
|
}
|
|
200
128
|
|
|
201
129
|
module.exports = RetoldDataService;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
var Chai = require("chai");
|
|
10
10
|
var Expect = Chai.expect;
|
|
11
|
+
var Assert = Chai.assert;
|
|
11
12
|
|
|
12
13
|
const libFable = require('fable');
|
|
13
14
|
|
|
@@ -41,13 +42,9 @@ suite
|
|
|
41
42
|
AutoStartOrator: true
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
tmpRetoldDataService.initializeService();
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
function(pError)
|
|
48
|
-
{
|
|
49
|
-
return fDone();
|
|
50
|
-
});
|
|
47
|
+
return fDone();
|
|
51
48
|
}
|
|
52
49
|
);
|
|
53
50
|
}
|