retold-data-service 2.0.12 → 2.0.13

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.
@@ -0,0 +1,38 @@
1
+ # onInitialize
2
+
3
+ Lifecycle hook called after Orator starts and the persistence engine loads, but before data endpoints are created.
4
+
5
+ ## Signature
6
+
7
+ `onInitialize(fCallback)`
8
+
9
+ ## Parameters
10
+
11
+ | Parameter | Type | Description |
12
+ |-----------|------|-------------|
13
+ | `fCallback` | Function | Callback `(pError)` to continue the initialization chain |
14
+
15
+ ## Default Behavior
16
+
17
+ No-op -- calls `fCallback()` immediately.
18
+
19
+ ## Override
20
+
21
+ ```javascript
22
+ class MyDataService extends require('retold-data-service')
23
+ {
24
+ onInitialize(fCallback)
25
+ {
26
+ this.fable.log.info('Running database migrations...');
27
+ // The persistence engine is connected but endpoints don't exist yet
28
+ return fCallback();
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Use Cases
34
+
35
+ - Database migrations
36
+ - Additional service registration
37
+ - Custom route registration before entity routes
38
+ - Schema transformations
@@ -0,0 +1,35 @@
1
+ # API Reference
2
+
3
+ Complete method reference for the `RetoldDataService` class.
4
+
5
+ ## Class: RetoldDataService
6
+
7
+ Extends `fable-serviceproviderbase`. Registered as service type `'RetoldDataService'`.
8
+
9
+ **Source:** `source/Retold-Data-Service.js`
10
+
11
+ ### Methods
12
+
13
+ | Method | Description |
14
+ |--------|-------------|
15
+ | [constructor](api/constructor.md) | Create a new RetoldDataService instance |
16
+ | [initializeService](api/initializeService.md) | Initialize the full service stack |
17
+ | [stopService](api/stopService.md) | Stop the Orator web server |
18
+ | [initializePersistenceEngine](api/initializePersistenceEngine.md) | Load the storage provider module |
19
+ | [initializeDataEndpoints](api/initializeDataEndpoints.md) | Load model and create DAL + endpoints |
20
+ | [onBeforeInitialize](api/onBeforeInitialize.md) | Pre-initialization lifecycle hook |
21
+ | [onInitialize](api/onInitialize.md) | Mid-initialization lifecycle hook |
22
+ | [onAfterInitialize](api/onAfterInitialize.md) | Post-initialization lifecycle hook |
23
+
24
+ ### Properties
25
+
26
+ | Property | Type | Description |
27
+ |----------|------|-------------|
28
+ | `serviceType` | String | Always `'RetoldDataService'` |
29
+ | `serviceInitialized` | Boolean | Whether the service has been initialized |
30
+ | `fullModel` | Object/Boolean | The loaded Stricture model, or `false` before init |
31
+ | `entityList` | Array/Boolean | Array of entity names, or `false` before init |
32
+ | `_DAL` | Object | Map of entity name to Meadow DAL instance |
33
+ | `_MeadowEndpoints` | Object | Map of entity name to MeadowEndpoints controller |
34
+ | `fable.DAL` | Object | Same as `_DAL` (decorated on Fable) |
35
+ | `fable.MeadowEndpoints` | Object | Same as `_MeadowEndpoints` (decorated on Fable) |
@@ -0,0 +1,34 @@
1
+ # stopService
2
+
3
+ Stop the Orator web server and reset the service state.
4
+
5
+ ## Signature
6
+
7
+ `stopService(fCallback)`
8
+
9
+ ## Parameters
10
+
11
+ | Parameter | Type | Description |
12
+ |-----------|------|-------------|
13
+ | `fCallback` | Function | Callback `(pError)` when the service is stopped |
14
+
15
+ ## Behavior
16
+
17
+ - Stops the Orator web server via `fable.Orator.stopWebServer()`
18
+ - Sets `serviceInitialized = false`
19
+ - Returns error if the service is not currently initialized
20
+
21
+ ## Example
22
+
23
+ ```javascript
24
+ _Fable.RetoldDataService.stopService(
25
+ (pError) =>
26
+ {
27
+ if (pError)
28
+ {
29
+ console.error('Stop failed:', pError);
30
+ return;
31
+ }
32
+ console.log('Service stopped');
33
+ });
34
+ ```
@@ -8,7 +8,7 @@ Retold Data Service orchestrates several Retold modules into a unified service t
8
8
  ┌─────────────────────────────────────────────┐
9
9
  │ Your Application Code │
10
10
  │ - Configure options │
11
- │ - Override lifecycle hooks
11
+ │ - Override lifecycle hooks
12
12
  │ - Inject behaviors │
13
13
  └──────────────────┬──────────────────────────┘
14
14
 
@@ -49,18 +49,66 @@ Retold Data Service orchestrates several Retold modules into a unified service t
49
49
  └─────────────────────────────────────────────┘
50
50
  ```
51
51
 
52
+ ## Initialization Flow
53
+
54
+ The `initializeService()` method runs an ordered sequence of asynchronous steps using Fable's Anticipate pattern:
55
+
56
+ ```mermaid
57
+ sequenceDiagram
58
+ participant App as Application
59
+ participant RDS as RetoldDataService
60
+ participant O as Orator
61
+ participant PE as PersistenceEngine
62
+ participant M as Meadow
63
+ participant ME as MeadowEndpoints
64
+
65
+ App->>RDS: initializeService()
66
+ RDS->>RDS: onBeforeInitialize()
67
+ RDS->>O: startWebServer()
68
+ RDS->>PE: initializePersistenceEngine()
69
+ RDS->>RDS: onInitialize()
70
+ RDS->>M: Load Stricture model
71
+ loop Each Entity
72
+ RDS->>M: loadFromPackageObject(schema)
73
+ M-->>RDS: DAL instance
74
+ RDS->>ME: new MeadowEndpoints(DAL)
75
+ ME->>O: connectRoutes()
76
+ end
77
+ RDS->>RDS: onAfterInitialize()
78
+ RDS-->>App: callback()
79
+ ```
80
+
81
+ ## Component Diagram
82
+
83
+ ```mermaid
84
+ graph TD
85
+ A[Application Code] --> B[RetoldDataService]
86
+ B --> C[Orator + Restify]
87
+ B --> D[Meadow DAL]
88
+ B --> E[MeadowEndpoints]
89
+ D --> F[FoxHound Query DSL]
90
+ D --> G[Storage Provider]
91
+ G --> H[MySQL]
92
+ G --> I[SQLite]
93
+ G --> J[MSSQL]
94
+ G --> K[PostgreSQL]
95
+ G --> L[MongoDB]
96
+ E --> M[REST Routes]
97
+ E --> N[Behavior Injection]
98
+ ```
99
+
52
100
  ## How It Works
53
101
 
54
- 1. **Registration** `RetoldDataService` is registered with Fable's service manager and instantiated with options
55
- 2. **Server Setup** Orator and its Restify service server are registered and configured
56
- 3. **Initialization** calling `initializeService()` triggers an ordered sequence:
57
- - `onBeforeInitialize()` your custom pre-initialization logic
102
+ 1. **Registration** -- `RetoldDataService` is registered with Fable's service manager and instantiated with options
103
+ 2. **Server Setup** -- Orator and its Restify service server are registered and configured
104
+ 3. **Initialization** -- calling `initializeService()` triggers an ordered sequence:
105
+ - `onBeforeInitialize()` -- your custom pre-initialization logic
58
106
  - Orator web server start (if `AutoStartOrator` is true)
59
107
  - Persistence engine initialization (loads the storage provider module)
60
- - `onInitialize()` your custom initialization logic
108
+ - `onInitialize()` -- your custom initialization logic
61
109
  - Data endpoint initialization (loads model, creates DALs and endpoints)
62
- - `onAfterInitialize()` your custom post-initialization logic
63
- 4. **Serving** the Restify server listens for HTTP requests and routes them through Meadow Endpoints to the DAL
110
+ - `onAfterInitialize()` -- your custom post-initialization logic
111
+ 4. **Serving** -- the Restify server listens for HTTP requests and routes them through Meadow Endpoints to the DAL
64
112
 
65
113
  ## Key Objects
66
114
 
@@ -83,12 +131,28 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
83
131
 
84
132
  class RetoldDataService extends libFableServiceProviderBase
85
133
  {
86
- constructor(pFable, pOptions, pServiceHash)
87
- {
88
- super(pFable, pOptions, pServiceHash);
89
- this.serviceType = 'RetoldDataService';
90
- }
134
+ constructor(pFable, pOptions, pServiceHash)
135
+ {
136
+ super(pFable, pOptions, pServiceHash);
137
+ this.serviceType = 'RetoldDataService';
138
+ }
91
139
  }
92
140
  ```
93
141
 
94
- This means it inherits logging, configuration access, and service lifecycle management from the base class.
142
+ This means it inherits logging, configuration access, and service lifecycle management from the base class. You register it with Fable's service manager like any other service:
143
+
144
+ ```javascript
145
+ const libRetoldDataService = require('retold-data-service');
146
+
147
+ _Fable.serviceManager.addServiceType('RetoldDataService', libRetoldDataService);
148
+ let _DataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
149
+ {
150
+ FullMeadowSchemaPath: `${__dirname}/model/`,
151
+ FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
152
+ StorageProvider: 'MySQL',
153
+ StorageProviderModule: 'meadow-connection-mysql',
154
+ AutoStartOrator: true
155
+ });
156
+ ```
157
+
158
+ Once instantiated, the service is available at `_Fable.RetoldDataService` and can be initialized with `initializeService()`.
@@ -1,111 +1,154 @@
1
1
  # Behavior Injection
2
2
 
3
- Behavior injection lets you add custom logic before and after any CRUD operation on your endpoints.
3
+ Behavior injection lets you add custom logic before or after any CRUD operation on any entity. This is the primary extension mechanism for adding business rules, validation, enrichment, and side effects.
4
4
 
5
5
  ## How It Works
6
6
 
7
- Each entity's Meadow Endpoints controller has a `BehaviorInjection` object that accepts pre- and post-operation hooks. These hooks run inline with the request handling pipeline.
7
+ Each MeadowEndpoints controller has a `BehaviorInjection` object that manages hooks for every CRUD operation. You set behaviors by specifying the operation and timing (pre or post).
8
+
9
+ ## Available Hooks
10
+
11
+ | Hook | Fires |
12
+ |------|-------|
13
+ | `Create-PreOperation` | Before a record is created |
14
+ | `Create-PostOperation` | After a record is created |
15
+ | `Read-PreOperation` | Before a single record is read |
16
+ | `Read-PostOperation` | After a single record is read |
17
+ | `Reads-PreOperation` | Before multiple records are read |
18
+ | `Reads-PostOperation` | After multiple records are read |
19
+ | `Update-PreOperation` | Before a record is updated |
20
+ | `Update-PostOperation` | After a record is updated |
21
+ | `Delete-PreOperation` | Before a record is deleted |
22
+ | `Delete-PostOperation` | After a record is deleted |
23
+ | `Count-PreOperation` | Before a count query |
24
+ | `Count-PostOperation` | After a count query |
8
25
 
9
26
  ## Setting a Behavior
10
27
 
11
28
  ```javascript
12
29
  _Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
13
- 'Read-PreOperation',
14
- (pRequest, pRequestState, fRequestComplete) =>
15
- {
16
- // Custom logic before the Read operation
17
- console.log('About to read Book ID:', pRequest.params.IDRecord);
18
- return fRequestComplete(false);
19
- });
30
+ 'Create-PreOperation',
31
+ (pRequest, pRequestState, fRequestComplete) =>
32
+ {
33
+ // Your custom logic here
34
+ return fRequestComplete(false);
35
+ });
20
36
  ```
21
37
 
22
- ## Available Hook Points
23
-
24
- Each CRUD operation supports `PreOperation` and `PostOperation` hooks:
25
-
26
- | Hook Name | Fires |
27
- |-----------|-------|
28
- | `Create-PreOperation` | Before a CREATE |
29
- | `Create-PostOperation` | After a CREATE |
30
- | `Read-PreOperation` | Before a single READ |
31
- | `Read-PostOperation` | After a single READ |
32
- | `Reads-PreOperation` | Before a READ-many |
33
- | `Reads-PostOperation` | After a READ-many |
34
- | `Update-PreOperation` | Before an UPDATE |
35
- | `Update-PostOperation` | After an UPDATE |
36
- | `Delete-PreOperation` | Before a DELETE |
37
- | `Delete-PostOperation` | After a DELETE |
38
- | `Count-PreOperation` | Before a COUNT |
39
- | `Count-PostOperation` | After a COUNT |
40
- | `Schema-PreOperation` | Before a SCHEMA read |
41
- | `Schema-PostOperation` | After a SCHEMA read |
42
-
43
- ## Request State
44
-
45
- The `pRequestState` object contains:
46
-
47
- | Property | Description |
48
- |----------|-------------|
49
- | `Record` | The current record being operated on |
50
- | `RecordToCreate` | The record being created (Create operations) |
51
- | `Query` | The FoxHound query object |
52
- | `SessionData` | Session data for the current request |
38
+ ## Callback Convention
39
+
40
+ The `fRequestComplete` callback accepts a single boolean argument:
41
+ - `false` -- Continue normal processing
42
+ - `true` -- Halt processing (request is already handled)
53
43
 
54
44
  ## Examples
55
45
 
56
- ### Enriching Read Results
46
+ ### Validation
57
47
 
58
48
  ```javascript
59
49
  _Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
60
- 'Read-PostOperation',
61
- (pRequest, pRequestState, fRequestComplete) =>
62
- {
63
- // Load related author data
64
- let tmpQuery = _Fable.DAL.BookAuthorJoin.query
65
- .addFilter('IDBook', pRequestState.Record.IDBook);
66
-
67
- _Fable.DAL.BookAuthorJoin.doReads(tmpQuery,
68
- (pError, pQuery, pJoinRecords) =>
69
- {
70
- pRequestState.Record.AuthorJoins = pJoinRecords;
71
- return fRequestComplete(false);
72
- });
73
- });
50
+ 'Create-PreOperation',
51
+ (pRequest, pRequestState, fRequestComplete) =>
52
+ {
53
+ if (!pRequestState.RecordToCreate.Title)
54
+ {
55
+ pRequest.CommonServices.sendCodedResponse(
56
+ pRequestState.response, 400, 'Title is required');
57
+ return fRequestComplete(true);
58
+ }
59
+ return fRequestComplete(false);
60
+ });
74
61
  ```
75
62
 
76
- ### Validating Before Create
63
+ ### Record Enrichment
77
64
 
78
65
  ```javascript
79
66
  _Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
80
- 'Create-PreOperation',
81
- (pRequest, pRequestState, fRequestComplete) =>
82
- {
83
- if (!pRequestState.RecordToCreate.Title)
84
- {
85
- pRequest.CommonServices.sendCodedResponse(
86
- pRequestState.response, 400, 'Title is required');
87
- return fRequestComplete(true); // true signals an error
88
- }
89
- return fRequestComplete(false);
90
- });
67
+ 'Read-PostOperation',
68
+ (pRequest, pRequestState, fRequestComplete) =>
69
+ {
70
+ // Add computed fields to the response
71
+ pRequestState.Record.DisplayTitle =
72
+ `${pRequestState.Record.Title} (${pRequestState.Record.PublicationYear})`;
73
+ return fRequestComplete(false);
74
+ });
91
75
  ```
92
76
 
93
- ### Adding Query Filters
77
+ ### Cross-Entity Enrichment
94
78
 
95
79
  ```javascript
96
80
  _Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
97
- 'Reads-PreOperation',
98
- (pRequest, pRequestState, fRequestComplete) =>
99
- {
100
- // Only return English-language books
101
- pRequestState.Query.addFilter('Language', 'English');
102
- return fRequestComplete(false);
103
- });
81
+ 'Read-PostOperation',
82
+ (pRequest, pRequestState, fRequestComplete) =>
83
+ {
84
+ _Fable.DAL.BookAuthorJoin.doReads(
85
+ _Fable.DAL.BookAuthorJoin.query.addFilter('IDBook', pRequestState.Record.IDBook),
86
+ (pJoinError, pJoinQuery, pJoinRecords) =>
87
+ {
88
+ let tmpAuthors = [];
89
+ let tmpRemaining = pJoinRecords.length;
90
+
91
+ if (tmpRemaining < 1)
92
+ {
93
+ pRequestState.Record.Authors = [];
94
+ return fRequestComplete(false);
95
+ }
96
+
97
+ for (let j = 0; j < pJoinRecords.length; j++)
98
+ {
99
+ _Fable.DAL.Author.doRead(
100
+ _Fable.DAL.Author.query.addFilter('IDAuthor', pJoinRecords[j].IDAuthor),
101
+ (pReadError, pReadQuery, pAuthor) =>
102
+ {
103
+ if (pAuthor && pAuthor.IDAuthor)
104
+ {
105
+ tmpAuthors.push(pAuthor);
106
+ }
107
+ tmpRemaining--;
108
+ if (tmpRemaining <= 0)
109
+ {
110
+ pRequestState.Record.Authors = tmpAuthors;
111
+ return fRequestComplete(false);
112
+ }
113
+ });
114
+ }
115
+ });
116
+ });
104
117
  ```
105
118
 
106
- ## Removing a Behavior
119
+ ### Audit Logging
120
+
121
+ ```javascript
122
+ _Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
123
+ 'Delete-PreOperation',
124
+ (pRequest, pRequestState, fRequestComplete) =>
125
+ {
126
+ _Fable.log.warn(`Book ${pRequestState.RecordToDelete.IDBook} is being deleted`);
127
+ return fRequestComplete(false);
128
+ });
129
+ ```
130
+
131
+ ## Timing
132
+
133
+ - **PreOperation** hooks run before the database operation. Use them for validation, transformation, or authorization.
134
+ - **PostOperation** hooks run after the database operation. Use them for enrichment, logging, or triggering side effects.
135
+
136
+ ## Using with Lifecycle Hooks
137
+
138
+ The best place to inject behaviors is in the `onAfterInitialize` lifecycle hook:
107
139
 
108
140
  ```javascript
109
- delete _Fable.MeadowEndpoints.Book.controller
110
- .BehaviorInjection._BehaviorFunctions['Read-PreOperation'];
141
+ class MyDataService extends require('retold-data-service')
142
+ {
143
+ onAfterInitialize(fCallback)
144
+ {
145
+ this.fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
146
+ 'Read-PostOperation', myBookEnrichmentHook);
147
+
148
+ this.fable.MeadowEndpoints.Review.controller.BehaviorInjection.setBehavior(
149
+ 'Create-PreOperation', myReviewValidationHook);
150
+
151
+ return fCallback();
152
+ }
153
+ }
111
154
  ```
@@ -0,0 +1,73 @@
1
+ /* ============================================================================
2
+ Pict Docuserve - Base Styles
3
+ ============================================================================ */
4
+
5
+ /* Reset and base */
6
+ *, *::before, *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ html, body {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
14
+ font-size: 16px;
15
+ line-height: 1.5;
16
+ color: #423D37;
17
+ background-color: #fff;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+ }
21
+
22
+ /* Typography */
23
+ h1, h2, h3, h4, h5, h6 {
24
+ margin-top: 0;
25
+ line-height: 1.3;
26
+ }
27
+
28
+ a {
29
+ color: #2E7D74;
30
+ text-decoration: none;
31
+ }
32
+
33
+ a:hover {
34
+ color: #256861;
35
+ }
36
+
37
+ /* Application container */
38
+ #Docuserve-Application-Container {
39
+ min-height: 100vh;
40
+ }
41
+
42
+ /* Utility: scrollbar styling */
43
+ ::-webkit-scrollbar {
44
+ width: 8px;
45
+ }
46
+
47
+ ::-webkit-scrollbar-track {
48
+ background: #F5F0E8;
49
+ }
50
+
51
+ ::-webkit-scrollbar-thumb {
52
+ background: #D4CCBE;
53
+ border-radius: 4px;
54
+ }
55
+
56
+ ::-webkit-scrollbar-thumb:hover {
57
+ background: #B5AA9A;
58
+ }
59
+
60
+ /* Responsive adjustments */
61
+ @media (max-width: 768px) {
62
+ html {
63
+ font-size: 14px;
64
+ }
65
+
66
+ #Docuserve-Sidebar-Container {
67
+ display: none;
68
+ }
69
+
70
+ .docuserve-body {
71
+ flex-direction: column;
72
+ }
73
+ }