retold-data-service 1.0.2 → 1.0.4

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.
Files changed (75) hide show
  1. package/.config/configstore/update-notifier-npm-check-updates.json +1 -1
  2. package/.config/configstore/update-notifier-npm.json +7 -1
  3. package/.config/vscode-sqltools/runningInfo.json +13 -0
  4. package/.vscode/launch.json +24 -0
  5. package/.vscode/settings.json +17 -0
  6. package/Dockerfile_LUXURYCode +94 -0
  7. package/README.md +21 -11
  8. package/debug/Harness.js +52 -2
  9. package/debug/bookstore-configuration.json +29 -0
  10. package/debug/data/books.csv +10001 -0
  11. package/debug/model/bookstore-api-endpoint-exercises.paw +0 -0
  12. package/debug/model/manual_scripts/MySQL-Laden-Entry.sh +17 -0
  13. package/debug/model/manual_scripts/MySQL-Security.sql +5 -0
  14. package/debug/model/manual_scripts/my.cnf +4 -0
  15. package/debug/model/sql_create/BookStore-CreateAndPopulateTables.sql +194 -0
  16. package/package.json +58 -41
  17. package/source/Retold-Data-Service.js +152 -288
  18. package/test/RetoldDataService_tests.js +20 -37
  19. package/test/model/fable-configuration.json +16 -0
  20. package/test/model/meadow/README.md +1 -0
  21. package/debug/Build-Database.sh +0 -12
  22. package/debug/Harness-Configuration.json +0 -51
  23. package/debug/model/doc/Dictionary.md +0 -18
  24. package/debug/model/doc/Model-Author.md +0 -20
  25. package/debug/model/doc/Model-Book.md +0 -26
  26. package/debug/model/doc/Model-BookAuthorJoin.md +0 -14
  27. package/debug/model/doc/Model-BookPrice.md +0 -25
  28. package/debug/model/doc/Model-Review.md +0 -22
  29. package/debug/model/doc/ModelChangeTracking.md +0 -17
  30. package/debug/model/doc/diagrams/Relationships.dot +0 -13
  31. package/debug/model/doc/diagrams/Relationships.png +0 -0
  32. package/debug/model/doc/diagrams/RelationshipsFull.dot +0 -13
  33. package/debug/model/doc/diagrams/RelationshipsFull.png +0 -0
  34. package/debug/model/mysql_create/MeadowModel-CreateMySQLDatabase.mysql.sql +0 -116
  35. package/source/Cumulation-Settings-Default.js +0 -19
  36. package/source/Cumulation.js +0 -90
  37. package/source/GraphGet.js +0 -607
  38. package/source/ProviderHelpers/Meadow-Provider-Helper-ALASQL.js +0 -48
  39. package/source/ProviderHelpers/Meadow-Provider-Helper-Base.js +0 -46
  40. package/source/ProviderHelpers/Meadow-Provider-Helper-MySQL.js +0 -62
  41. package/test/basic_test_configurations/fable-config-load_model.json +0 -45
  42. package/test/model/meadow_model/BookStore-PICT.json +0 -1
  43. package/test/model/meadow_model/README.md +0 -1
  44. /package/debug/model/{MeadowModel-Extended.json → Model-Extended.json} +0 -0
  45. /package/debug/model/{MeadowModel-PICT.json → Model-PICT.json} +0 -0
  46. /package/debug/model/{MeadowModel.json → Model.json} +0 -0
  47. /package/debug/{Model.ddl → model/ddl/BookStore.ddl} +0 -0
  48. /package/{test → debug}/model/generated_diagram/README.md +0 -0
  49. /package/{test → debug}/model/generated_diagram/Stricture_Output.dot +0 -0
  50. /package/{test → debug}/model/generated_diagram/Stricture_Output.png +0 -0
  51. /package/{test → debug}/model/generated_documentation/Dictionary.md +0 -0
  52. /package/{test → debug}/model/generated_documentation/Model-Author.md +0 -0
  53. /package/{test → debug}/model/generated_documentation/Model-Book.md +0 -0
  54. /package/{test → debug}/model/generated_documentation/Model-BookAuthorJoin.md +0 -0
  55. /package/{test → debug}/model/generated_documentation/Model-BookPrice.md +0 -0
  56. /package/{test → debug}/model/generated_documentation/Model-Review.md +0 -0
  57. /package/{test → debug}/model/generated_documentation/ModelChangeTracking.md +0 -0
  58. /package/{test → debug}/model/generated_documentation/README.md +0 -0
  59. /package/{test → debug}/model/manual_scripts/DropTables.sql +0 -0
  60. /package/{test → debug}/model/manual_scripts/README.md +0 -0
  61. /package/debug/model/meadow/{MeadowSchemaAuthor.json → Model-MeadowSchema-Author.json} +0 -0
  62. /package/debug/model/meadow/{MeadowSchemaBook.json → Model-MeadowSchema-Book.json} +0 -0
  63. /package/debug/model/meadow/{MeadowSchemaBookAuthorJoin.json → Model-MeadowSchema-BookAuthorJoin.json} +0 -0
  64. /package/debug/model/meadow/{MeadowSchemaBookPrice.json → Model-MeadowSchema-BookPrice.json} +0 -0
  65. /package/debug/model/meadow/{MeadowSchemaReview.json → Model-MeadowSchema-Review.json} +0 -0
  66. /package/{test/model/meadow_schema → debug/model/meadow}/README.md +0 -0
  67. /package/{test → debug}/model/sql_create/BookStore-CreateDatabase.mysql.sql +0 -0
  68. /package/{test → debug}/model/sql_create/README.md +0 -0
  69. /package/test/model/{meadow_model/BookStore-Extended.json → Model-Extended.json} +0 -0
  70. /package/test/model/{meadow_model/BookStore.json → Model.json} +0 -0
  71. /package/test/model/{meadow_schema/BookStore-MeadowSchema-Author.json → meadow/Model-MeadowSchema-Author.json} +0 -0
  72. /package/test/model/{meadow_schema/BookStore-MeadowSchema-Book.json → meadow/Model-MeadowSchema-Book.json} +0 -0
  73. /package/test/model/{meadow_schema/BookStore-MeadowSchema-BookAuthorJoin.json → meadow/Model-MeadowSchema-BookAuthorJoin.json} +0 -0
  74. /package/test/model/{meadow_schema/BookStore-MeadowSchema-BookPrice.json → meadow/Model-MeadowSchema-BookPrice.json} +0 -0
  75. /package/test/model/{meadow_schema/BookStore-MeadowSchema-Review.json → meadow/Model-MeadowSchema-Review.json} +0 -0
package/package.json CHANGED
@@ -1,43 +1,60 @@
1
1
  {
2
- "name": "retold-data-service",
3
- "version": "1.0.2",
4
- "description": "Serve up a whole model!",
5
- "main": "source/Retold-Data-Service.js",
6
- "scripts": {
7
- "start": "node source/Retold-Data-Service.js",
8
- "coverage": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- -u tdd -R spec",
9
- "test": "./node_modules/mocha/bin/_mocha -u tdd -R spec",
10
- "harness": "node debug/Harness.js"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "https://github.com/stevenvelozo/retold-data-service.git"
15
- },
16
- "keywords": [
17
- "entity",
18
- "behavior"
19
- ],
20
- "author": "Steven Velozo <steven@velozo.com> (http://velozo.com/)",
21
- "license": "MIT",
22
- "bugs": {
23
- "url": "https://github.com/stevenvelozo/retold-data-service/issues"
24
- },
25
- "homepage": "https://github.com/stevenvelozo/retold-data-service",
26
- "devDependencies": {
27
- "chai": "3.5.0",
28
- "coveralls": "2.13.1",
29
- "istanbul": "0.4.5",
30
- "mocha": "3.4.1"
31
- },
32
- "dependencies": {
33
- "async": "^3.2.3",
34
- "cookie": "^0.5.0",
35
- "fable": "^2.0.5",
36
- "meadow-endpoints": "^3.0.2",
37
- "orator": "^2.0.4",
38
- "restify": "^8.6.1",
39
- "simple-get": "^4.0.1",
40
- "underscore": "^1.13.2",
41
- "mysql2": "1.2.0"
42
- }
2
+ "name": "retold-data-service",
3
+ "version": "1.0.4",
4
+ "description": "Serve up a whole model!",
5
+ "main": "source/Retold-Data-Service.js",
6
+ "scripts": {
7
+ "start": "node source/Retold-Data-Service.js",
8
+ "coverage": "./node_modules/.bin/nyc --reporter=lcov --reporter=text-lcov ./node_modules/mocha/bin/_mocha -- -u tdd -R spec",
9
+ "test": "./node_modules/.bin/mocha -u tdd -R spec",
10
+ "build": "npx quack build",
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 8086: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
+ "docker-dev-shell": "docker exec -it retold-data-service-dev /bin/bash"
14
+ },
15
+ "mocha": {
16
+ "diff": true,
17
+ "extension": [
18
+ "js"
19
+ ],
20
+ "package": "./package.json",
21
+ "reporter": "spec",
22
+ "slow": "75",
23
+ "timeout": "5000",
24
+ "ui": "tdd",
25
+ "watch-files": [
26
+ "source/**/*.js",
27
+ "test/**/*.js"
28
+ ],
29
+ "watch-ignore": [
30
+ "lib/vendor"
31
+ ]
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/stevenvelozo/retold-data-service.git"
36
+ },
37
+ "keywords": [
38
+ "entity",
39
+ "behavior",
40
+ "api"
41
+ ],
42
+ "author": "Steven Velozo <steven@velozo.com> (http://velozo.com/)",
43
+ "license": "MIT",
44
+ "bugs": {
45
+ "url": "https://github.com/stevenvelozo/retold-data-service/issues"
46
+ },
47
+ "homepage": "https://github.com/stevenvelozo/retold-data-service",
48
+ "devDependencies": {
49
+ "quackage": "^1.0.24"
50
+ },
51
+ "dependencies": {
52
+ "fable": "^3.0.96",
53
+ "fable-serviceproviderbase": "^3.0.12",
54
+ "meadow": "^2.0.15",
55
+ "meadow-connection-mysql": "^1.0.4",
56
+ "meadow-endpoints": "^4.0.2",
57
+ "orator": "^4.0.2",
58
+ "orator-serviceserver-restify": "^2.0.2"
59
+ }
43
60
  }
@@ -1,336 +1,200 @@
1
- // ##### Part of the **[retold](https://stevenvelozo.github.io/retold/)** system
2
1
  /**
3
- * @license MIT
4
- * @author <steven@velozo.com>
5
- */
6
- const libAsync = require('async');
2
+ * Retold Data Service
3
+ *
4
+ * All-in-one add-ins for fable to provide endpoints.
5
+ *
6
+ * @author Steven Velozo <steven@velozo.com>
7
+ */
8
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
9
+
7
10
  const libOrator = require('orator');
11
+ const libOratorServiceServerRestify = require('orator-serviceserver-restify');
8
12
  const libMeadow = require('meadow');
9
13
  const libMeadowEndpoints = require('meadow-endpoints');
10
14
 
11
- const libProviderBase = require('./ProviderHelpers/Meadow-Provider-Helper-Base.js');
12
-
13
- const libGraphSolver = require('./GraphGet.js');
14
- const libCumulation = require('./Cumulation.js');
15
- /**
16
- * Retold Data Service
17
- *
18
- * Load all schemas from a model, and serve up the endpoints. Provide join autofills, graph
19
- * solver and other data niceties.
20
- *
21
- * Configuration stanza expectation:
22
- RetoldDataService: {
23
- SchemaFile: 'SchemaFileLocation',
24
- -- OR --
25
- Schema: [{ ... schemas in here ... }, { ... more schemas ... }],
26
-
27
- AutoLoad: true,
28
-
29
- MapExtraEndpoints: true,
15
+ const defaultDataServiceSettings = (
16
+ {
17
+ FullMeadowSchemaPath: `${process.cwd()}/model/`,
18
+ FullMeadowSchemaFilename: `Model-Extended.json`,
30
19
 
31
- SchemaGraphEndpoint: 'SchemaGraph'
32
- }
20
+ DALMeadowSchemaPath: `${process.cwd()}/model/meadow/`,
21
+ DALMeadowSchemaPrefix: `Model-MeadowSchema-`,
22
+ DALMeadowSchemaPostfix: ``,
33
23
 
24
+ AutoStartOrator: true
25
+ });
34
26
 
35
- * The first four settings are very obvious what they do. The last one is the endpoint that
36
- * serves the entire schema graph.
37
- *
38
- *
39
- * @class RetoldDataService
40
- */
41
- class RetoldDataService
27
+ class RetoldDataService extends libFableServiceProviderBase
42
28
  {
43
- constructor(pConfiguration)
29
+ constructor(pFable, pManifest, pServiceHash)
44
30
  {
45
- let tmpConfiguration = (typeof(pConfiguration) === 'object') ? pConfiguration : {};
46
-
47
- this._Orator = libOrator.new(tmpConfiguration);
48
-
49
- this._Fable = this._Orator.fable;
50
- this._Settings = this._Fable.settings;
51
-
52
- this._Meadow = libMeadow.new(this._Fable);
53
- this._MeadowModelGraph = {};
31
+ super(pFable, pManifest, pServiceHash);
54
32
 
55
- this._DAL = {};
56
- this._MeadowEndpoints = {};
33
+ this.serviceType = 'RetoldDataService';
57
34
 
58
- this._Providers = {};
59
-
60
- this._GraphSolver = false;
35
+ this.options = this.fable.Utility.extend(defaultDataServiceSettings, this.options);
36
+ }
61
37
 
62
- this.log = this._Fable.log;
38
+ onBeforeInitializeAsync(fCallback)
39
+ {
40
+ this.onBeforeInitialize();
41
+ return fCallback();
42
+ }
43
+ onBeforeInitialize()
44
+ {
45
+ this.log.info("Retold Data Service Application is beginning initialization...");
46
+ }
63
47
 
64
- // Load the configurations
65
- if (this._Settings.hasOwnProperty('Retold'))
48
+ initializeAsync(fCallback)
49
+ {
50
+ if (this.serviceInitialized)
66
51
  {
67
- if (this._Settings.Retold.hasOwnProperty('MeadowModel'))
68
- {
69
- // One or many full Model file path(s) has been set
70
- this._Settings.Retold.MeadowModel = `${process.cwd()}/model/MeadowModel.json`;
71
- }
72
-
73
- if (this._Settings.Retold.hasOwnProperty('MeadowEntitySchemaPrefix'))
74
- {
75
- // One or many Entity Schema file path(s) is set
76
- this._Settings.Retold.MeadowEntitySchemaPrefix = `${process.cwd()}/model/meadow/MeadowSchema`;
77
- }
52
+ this.fable.log.error("Retold Data Service Application is being initialized but has already been initialized...");
78
53
  }
79
54
  else
80
55
  {
81
- // Create a retold entry with defaults
82
- this._Settings.Retold = (
83
- {
84
- // This setting will automatically load the model
85
- "AutoLoadModel": true,
86
- // This setting will automatically connect the provider via the helper
87
- // (e.g. connect to MySQL)
88
- "AutoConnectProvider": true,
89
- // This setting will automatically connect the backplane endpoints
90
- "AutoMapBackplaneEndpoints": true,
91
- // This setting will automatically map the entity endpoints when entities are loaded
92
- "AutoMapEntityEndpoints": true,
93
- // This setting autostarts the orator server
94
- "AutoStartAPIServer": true,
95
-
96
- // These allow the deeper behaviors of the provider helpers (e.g. creating tables)
97
- "AllowDatabaseCreate": false,
98
- "AllowTableCreate": false,
99
- "AllowTableUpdate": false,
100
-
101
- // This will never work because the base provider just uses English.
102
- // Maybe switch to ALASQL by default for stateless services.
103
- "DefaultMeadowDataProvider": "Base",
104
-
105
- // Either the Meadow side or Stricture side should be set.
106
- // Try to use the opinionated Stricture locations if they arent (you really should set this though!)
107
- "MeadowModel": `${process.cwd()}/model/MeadowModel.json`,
108
- "MeadowEntitySchemaPrefix": `${process.cwd()}/model/meadow/MeadowSchema`,
109
-
110
- "StrictureDDL": false
111
- });
112
- }
113
-
114
- this.initialize();
115
- }
116
-
117
- initialize(fCallback)
118
- {
119
- let tmpCallback = (typeof(fCallback) == 'function') ? fCallback : ()=>{};
120
- libAsync.waterfall(
121
- [
122
- (fStageComplete) =>
56
+ let tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');
57
+ // Phase I: Allow the API host to inject behavior before anything magic happens.
58
+ tmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));
59
+ // Phase II: Make sure there is an Orator server available so we can put the behaviors somewhere
60
+ tmpAnticipate.anticipate(
61
+ function (fCallback)
123
62
  {
124
- if (this._Settings.Retold.AutoLoadModel)
63
+ // Check if Fable already has an Orator service provider, otherwise add one.
64
+ if (!this.fable.serviceClasses.hasOwnProperty('OratorServiceServer'))
125
65
  {
126
- this.loadModels();
66
+ this.fable.serviceManager.addAndInstantiateServiceType('OratorServiceServer', libOratorServiceServerRestify, this.fable.settings);
127
67
  }
128
-
129
- return fStageComplete();
130
- },
131
- (fStageComplete) =>
132
- {
133
- if (this._Settings.Retold.AutoConnectProvider)
68
+ else if (!this.fable.OratorServiceServer)
134
69
  {
135
- this.initializeDefaultProvider();
70
+ this.fable.serviceManager.instantiateServiceProvider('OratorServiceServer', this.fable.settings);
136
71
  }
137
- return fStageComplete();
138
- },
139
- (fStageComplete) =>
72
+ return fCallback();
73
+ }.bind(this));
74
+ tmpAnticipate.anticipate(
75
+ function (fCallback)
140
76
  {
141
- if (this._Settings.Retold.AutoMapBackplaneEndpoints)
77
+ // Check if Fable already has an Orator service provider, otherwise add one.
78
+ if (!this.fable.serviceClasses.hasOwnProperty('Orator'))
142
79
  {
143
- this.mapBackplaneEndpoints();
80
+ this.fable.serviceManager.addAndInstantiateServiceType('Orator', libOrator, this.fable.settings);
144
81
  }
145
-
146
- return fStageComplete();
147
- },
148
- (fStageComplete) =>
82
+ else if (!this.fable.Orator)
83
+ {
84
+ this.fable.serviceManager.instantiateServiceProvider('Orator', this.fable.settings);
85
+ }
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)
149
93
  {
150
- // Initialize the graph solver library
151
- this.initializeGraphEndpoints();
152
- // Map the graph endpoints to it
153
- // TODO: Decide if this follows the Meadow pattern or some other prefix pattern
154
- this._Orator.webServer.post(`/1.0/GraphRead/:Entity`,
155
- (pRequest, pResponse, fNext) =>
156
- {
157
- let tmpGraphRequestEntity = pRequest.params.Entity;
158
- let tmpGraphRequestFilter = pRequest.body;
159
-
160
- // TODO: Discuss how to pass in request settings -- what was in there worked for the web client and works for tokens so maybe it's okay?
161
- let tmpCumulation = new libCumulation(this._Fable, {});
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
+
97
+ // Create DAL objects for each table in the schema
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.
108
+ // Load the mysql connection for meadow if it doesn't exist yet
109
+ _Fable.serviceManager.addAndInstantiateServiceType('MeadowMySQLProvider', require('meadow-connection-mysql'));
110
+
111
+ this.fullModel = false;
112
+ this.entityList = false;
113
+
114
+ this.serviceInitialized = false;
115
+
116
+ return fCallback();
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...");
162
123
 
163
- this._GraphSolver.get(tmpGraphRequestEntity, tmpGraphRequestFilter, tmpCumulation,
164
- (pError, pRecords, pValidFilters, pFinalFilters, pJoinedDataSets, pValidIdentities) =>
165
- {
166
- pResponse.send(
167
- {
168
- GraphRequestEntity: tmpGraphRequestEntity,
169
- GraphRequestFilter: tmpGraphRequestFilter,
124
+ // Create DAL objects for each table in the schema
125
+ // 1. Load full compiled schema of the model from stricture
126
+ _Fable.log.info(`...loading full model stricture schema...`);
127
+ this.fullModel = require (`${this.options.FullMeadowSchemaPath}${this.options.FullMeadowSchemaFilename}`);
128
+ _Fable.log.info(`...full model stricture schema loaded.`);
170
129
 
171
- Records: pRecords,
130
+ // 2. Extract an array of each table in the schema
131
+ _Fable.log.info(`...getting entity list...`);
132
+ this.entityList = Object.keys(this.fullModel.Tables);
172
133
 
173
- ValidFilters: pValidFilters,
174
- Finalfilters: pFinalFilters,
134
+ // 3. Enumerate each entry in the compiled model and load a DAL for that table
135
+ _Fable.log.info(`...initializing ${this.entityList.length} DAL objects and corresponding Meadow Endpoints...`);
136
+ for (let i = 0; i < this.entityList.length; i++)
137
+ {
138
+ // 4. Create the DAL for each entry (e.g. it would be at _DAL.Movie for the Movie entity)
139
+ let tmpDALEntityName = this.entityList[i];
140
+ let tmpDALPackageFile = `${this.options.DALMeadowSchemaPath}${this.options.DALMeadowSchemaPrefix}${tmpDALEntityName}${this.options.DALMeadowSchemaPostfix}.json`
141
+ _Fable.log.info(`Initializing the ${tmpDALEntityName} DAL from [${tmpDALPackageFile}]...`);
142
+ this._DAL[tmpDALEntityName] = this._Meadow.loadFromPackage(tmpDALPackageFile);
143
+ // 5. Tell this DAL object to use MySQL
144
+ _Fable.log.info(`...defaulting the ${tmpDALEntityName} DAL to use MySQL`);
145
+ this._DAL[tmpDALEntityName].setProvider('MySQL');
146
+ // 6. Create a Meadow Endpoints class for this DAL
147
+ _Fable.log.info(`...initializing the ${tmpDALEntityName} Meadow Endpoints to use MySQL`);
148
+ this._MeadowEndpoints[tmpDALEntityName] = libMeadowEndpoints.new(this._DAL[tmpDALEntityName]);
149
+ // 8. Expose the meadow endpoints on Orator
150
+ _Fable.log.info(`...mapping the ${tmpDALEntityName} Meadow Endpoints to Orator`);
151
+ this._MeadowEndpoints[tmpDALEntityName].connectRoutes(this.fable.Orator.webServer);
152
+ }
175
153
 
176
- JoinedDataSets: pJoinedDataSets,
177
- ValidIdentities: pValidIdentities,
154
+ this.serviceInitialized = true;
178
155
 
179
- Error: pError
180
- });
181
- return fNext();
182
- });
183
- });
184
-
185
- return fStageComplete();
186
- },
187
- (fStageComplete) =>
156
+ return fCallback();
157
+ }.bind(this));
158
+ // Phase VI: Optionally auto start the orator server.
159
+ tmpAnticipate.anticipate(
160
+ function (fCallback)
188
161
  {
189
- if (this._Settings.Retold.AutoStartAPIServer)
162
+ if (this.options.AutoStartOrator)
190
163
  {
191
- this.start(fStageComplete);
164
+ this.fable.log.info(`The Retold Data Service is Auto Starting Orator...`);
165
+ this.fable.Orator.startWebServer(fCallback);
192
166
  }
193
167
  else
194
168
  {
195
- return fStageComplete();
169
+ return fCallback();
196
170
  }
197
- }
198
- ],
199
- (pError)=>
200
- {
201
- if (pError)
202
- {
203
- this._Fable.log.error('Error initializing Retold Data Service: '+pError.toString(), pError);
204
- }
205
-
206
- tmpCallback(pError);
207
- });
208
- }
209
-
210
- initializeDefaultProvider()
211
- {
212
- let libProvider = require(`./ProviderHelpers/Meadow-Provider-Helper-${this._Settings.Retold.DefaultMeadowDataProvider}.js`);
213
-
214
- // This is to support multiple providers
215
- this._Providers.Default = new libProvider(this._Fable);
216
-
217
- return this._Providers.Default.connect();
218
- }
219
-
220
- initializeGraphEndpoints()
221
- {
222
- this._GraphSolver = new libGraphSolver(this._Fable, this._MeadowModelGraph);
223
- }
224
-
225
- mapBackplaneEndpoints()
226
- {
227
- const tmpBackplanePrefix = 'BACKPLANE';
228
- // Pull version from the config file.
229
- const tmpEndpointVersion = this._Fable.settings.MeadowEndpointVersion || '1.0';
230
-
231
- const tmpBackplaneEndpointPrefix = `/${tmpBackplanePrefix}/${tmpEndpointVersion}`;
232
-
233
- // TODO: Break this out into separate classes
234
- this._Orator.webServer.get(`${tmpBackplaneEndpointPrefix}/Model`,
235
- (pRequest, pResponse, fNext) =>
236
- {
237
- pResponse.send(this._MeadowModelGraph);
238
- return fNext();
239
- });
240
- }
241
-
242
- // Lock the service to a specific session (this will bypass auth)
243
- lockServiceSession(pSessionData)
244
- {
245
- if (typeof(pSessionData) != 'object')
246
- {
247
- this.log.error(`Error locking service session -- invalid object passed in.`, pSessionData);
248
- return false;
249
- }
250
-
251
- _Fable.log.info(`...Creating Mock User Session and adding it to all requests on Restify/Orator`, pSessionData);
252
- var fMockAuthentication = (pRequest, pResponse, fNext) =>
253
- {
254
- pRequest.UserSession = pSessionData;
255
- fNext();
256
- };
257
- this._Orator.webServer.use(fMockAuthentication);
258
- }
259
-
260
- // Load models based on what's in the configuration
261
- loadModels()
262
- {
263
- if (this._Settings.Retold.MeadowModel)
264
- {
265
- this.loadMeadowModelFile(this._Settings.Retold.MeadowModel);
266
- }
267
- }
171
+ }.bind(this));
172
+ tmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));
268
173
 
269
- loadMeadowModelFile(pModelFilePath)
270
- {
271
- // TODO: Wrap this in a try/catch/finally
272
- this._MeadowModelGraph = require(pModelFilePath);
273
- this.loadMeadowModel(this._MeadowModelGraph);
274
- }
275
-
276
- loadMeadowModel(pModelObject)
277
- {
278
- // Create DAL objects for each table in the schema
279
- // 1. Validate the model object
280
- if (typeof(pModelObject) !== 'object')
281
- {
282
- this.log.error(`Could not load Meadow model object: Invalid object passed.`, pModelObject);
283
- return false;
284
- }
285
- if (!pModelObject.hasOwnProperty('Tables')
286
- || typeof(pModelObject.Tables) != 'object')
287
- {
288
- this.log.error(`Could not load Meadow model object: Object does not contain a Tables property.`, pModelObject);
289
- return false;
290
- }
291
- // 2. Extract an array of each table in the schema
292
- let tmpModelTableList = Object.keys(pModelObject.Tables);
293
- let tmpDefaultProvider = (this._Fable.settings.Retold.DefaultMeadowDataProvider) ? this._Fable.settings.Retold.DefaultMeadowDataProvider : 'Base';
294
- // 3. Enumerate each entry in the compiled model and load a DAL for that table
295
- this.log.info(`...Creating ${tmpModelTableList.length} DAL entries...`);
296
- for (let i = 0; i < tmpModelTableList.length; i++)
297
- {
298
- let tmpDALEntityName = tmpModelTableList[i];
299
- this.log.info(` -> Creating the [${tmpDALEntityName}] DAL...`);
300
- // 4. Create the DAL for each entry (e.g. it would be at _DAL.Book or _DAL.Author)
301
- // TODO: I don't like this ... stricture should just generate a huge file of these in an array, which could be loaded with the new injector
302
- this._DAL[tmpDALEntityName] = this._Meadow.loadFromPackage(`${this._Settings.Retold.MeadowEntitySchemaPrefix}${tmpDALEntityName}.json`);
303
- // 5. Tell this DAL object to use the default provider
304
- this._DAL[tmpDALEntityName].setProvider(tmpDefaultProvider);
305
- // 6. Create a Meadow Endpoints class for this DAL
306
- this._MeadowEndpoints[tmpDALEntityName] = libMeadowEndpoints.new(this._DAL[tmpDALEntityName]);
307
- // 7. Expose the meadow endpoints on Orator
308
- if (this._Fable.settings.Retold.AutoMapEntityEndpoints)
309
- {
310
- this._MeadowEndpoints[tmpDALEntityName].connectRoutes(this._Orator.webServer);
311
- this._MeadowEndpoints[tmpDALEntityName].RoutesConnected = true;
312
- }
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));
313
187
  }
314
188
  }
315
189
 
316
- start(fCallback)
317
- {
318
- this._Orator.startWebServer(fCallback);
319
- }
320
-
321
- get orator()
190
+ onAfterInitializeAsync(fCallback)
322
191
  {
323
- return this._Orator;
192
+ this.onAfterInitialize();
193
+ return fCallback();
324
194
  }
325
-
326
- get fable()
327
- {
328
- return this._Fable;
329
- }
330
-
331
- get settings()
195
+ onAfterInitialize()
332
196
  {
333
- return this._Fable.settings;
197
+ this.log.info("Retold Data Service Application has completed an initialization attempt.");
334
198
  }
335
199
  }
336
200
 
@@ -8,9 +8,10 @@
8
8
 
9
9
  var Chai = require("chai");
10
10
  var Expect = Chai.expect;
11
- var Assert = Chai.assert;
12
11
 
13
- var libRetoldDataService = require('../source/Retold-Data-Service.js');
12
+ const libFable = require('fable');
13
+
14
+ const _Settings = require(`./model/fable-configuration.json`);
14
15
 
15
16
  suite
16
17
  (
@@ -24,47 +25,29 @@ suite
24
25
  'Object Sanity',
25
26
  function()
26
27
  {
27
- test
28
- (
29
- 'The class should initialize itself into a happy little object.',
30
- function()
31
- {
32
- testService = new libRetoldDataService({"Product":"SimpleInitializationTest"});
33
-
34
- // Instantiate the logger
35
- Expect(testService).to.be.an('object', 'The data service should initialize as an object directly from the require statement and minimal configuration.');
36
- Expect(testService).to.have.a.property('log')
37
- .that.is.a('object');
38
- Expect(testService).to.have.a.property('settings')
39
- .that.is.a('object');
40
- Expect(testService).to.have.a.property('fable')
41
- .that.is.a('object');
42
- Expect(testService).to.have.a.property('orator')
43
- .that.is.a('object');
44
- Expect(testService.settings.Product)
45
- .to.equal('SimpleInitializationTest')
46
- }
47
- );
48
28
  test
49
29
  (
50
30
  'Change some settings later...',
51
31
  function(fDone)
52
32
  {
53
- testService = new libRetoldDataService({"Product":"SimpleSettingsTest"});
33
+ _Fable = new libFable(_Settings);
34
+ _Fable.serviceManager.addServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
35
+ let tmpRetoldDataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
36
+ {
37
+ FullMeadowSchemaPath: `${process.cwd()}/test/model/`,
38
+ DALMeadowSchemaPath: `${process.cwd()}/test/model/meadow/`,
39
+
40
+ AutoInitializeDataService: true,
41
+ AutoStartOrator: true
42
+ });
43
+
44
+ Expect(tmpRetoldDataService).to.be.an('object', 'Retold Data Service should initialize as an object.');
54
45
 
55
- Expect(testService).to.have.a.property('settings')
56
- .that.is.a('object');
57
- Expect(testService.settings.Product)
58
- .to.equal('SimpleSettingsTest');
59
- Expect(testService.settings.ProductVersion)
60
- .to.equal('0.0.0');
61
- // Now change a setting by merging in something new
62
- testService.fable.settingsManager.merge({Product:'TestProduct'});
63
- Expect(testService.settings.Product)
64
- .to.equal('TestProduct');
65
- Expect(testService.settings.ProductVersion)
66
- .to.equal('0.0.0');
67
- fDone();
46
+ tmpRetoldDataService.initializeAsync(
47
+ function(pError)
48
+ {
49
+ return fDone();
50
+ });
68
51
  }
69
52
  );
70
53
  }