retold-data-service 2.0.9 → 2.0.11
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/CONTRIBUTING.md +50 -0
- package/README.md +200 -56
- package/docs/.nojekyll +0 -0
- package/docs/README.md +76 -0
- package/docs/_sidebar.md +13 -0
- package/docs/architecture.md +94 -0
- package/docs/behavior-injection.md +111 -0
- package/docs/configuration.md +93 -0
- package/docs/cover.md +11 -0
- package/docs/dal-access.md +87 -0
- package/docs/endpoints.md +121 -0
- package/docs/index.html +39 -0
- package/docs/initialization.md +77 -0
- package/docs/lifecycle-hooks.md +81 -0
- package/docs/schema-definition.md +128 -0
- package/docs/storage-providers.md +109 -0
- package/package.json +10 -6
- package/source/Retold-Data-Service.js +4 -4
- package/test/RetoldDataService_tests.js +1315 -18
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Retold Data Service accepts options during instantiation that control model loading, storage provider selection, and server behavior.
|
|
4
|
+
|
|
5
|
+
## Options Reference
|
|
6
|
+
|
|
7
|
+
| Option | Type | Default | Description |
|
|
8
|
+
|--------|------|---------|-------------|
|
|
9
|
+
| `StorageProvider` | String | `'MySQL'` | Meadow provider name (`MySQL`, `SQLite`, `MSSQL`, `ALASQL`) |
|
|
10
|
+
| `StorageProviderModule` | String | `'meadow-connection-mysql'` | npm module name for the storage provider |
|
|
11
|
+
| `FullMeadowSchemaPath` | String | `${process.cwd()}/model/` | Directory path where the compiled schema lives |
|
|
12
|
+
| `FullMeadowSchemaFilename` | String | `'MeadowModel-Extended.json'` | Filename of the compiled Stricture schema |
|
|
13
|
+
| `AutoInitializeDataService` | Boolean | `true` | Whether to auto-initialize on construction |
|
|
14
|
+
| `AutoStartOrator` | Boolean | `true` | Whether to start the Orator web server during initialization |
|
|
15
|
+
|
|
16
|
+
## Fable Settings
|
|
17
|
+
|
|
18
|
+
The Fable instance configuration controls server port and database connection:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
{
|
|
22
|
+
Product: 'MyApp',
|
|
23
|
+
APIServerPort: 8086,
|
|
24
|
+
|
|
25
|
+
// For MySQL
|
|
26
|
+
MySQL: {
|
|
27
|
+
Server: '127.0.0.1',
|
|
28
|
+
Port: 3306,
|
|
29
|
+
User: 'root',
|
|
30
|
+
Password: 'secret',
|
|
31
|
+
Database: 'mydb',
|
|
32
|
+
ConnectionPoolLimit: 20
|
|
33
|
+
},
|
|
34
|
+
MeadowConnectionMySQLAutoConnect: true,
|
|
35
|
+
|
|
36
|
+
// For SQLite
|
|
37
|
+
SQLite: {
|
|
38
|
+
SQLiteFilePath: './data/myapp.db'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Example: MySQL Configuration
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
_Fable.serviceManager.instantiateServiceProvider('RetoldDataService', {
|
|
47
|
+
FullMeadowSchemaPath: `${__dirname}/model/`,
|
|
48
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
49
|
+
StorageProvider: 'MySQL',
|
|
50
|
+
StorageProviderModule: 'meadow-connection-mysql',
|
|
51
|
+
AutoStartOrator: true
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Example: SQLite Configuration
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Register the SQLite provider before creating the data service
|
|
59
|
+
const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
|
|
60
|
+
_Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
|
|
61
|
+
_Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
|
|
62
|
+
|
|
63
|
+
_Fable.MeadowSQLiteProvider.connectAsync(
|
|
64
|
+
(pError) =>
|
|
65
|
+
{
|
|
66
|
+
_Fable.serviceManager.instantiateServiceProvider('RetoldDataService', {
|
|
67
|
+
FullMeadowSchemaPath: `${__dirname}/model/`,
|
|
68
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
69
|
+
StorageProvider: 'SQLite',
|
|
70
|
+
StorageProviderModule: 'meadow-connection-sqlite',
|
|
71
|
+
AutoStartOrator: true
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Example: Disabling Auto-Start
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
_Fable.serviceManager.instantiateServiceProvider('RetoldDataService', {
|
|
80
|
+
FullMeadowSchemaPath: `${__dirname}/model/`,
|
|
81
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
82
|
+
AutoStartOrator: false
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Manually start later
|
|
86
|
+
_Fable.RetoldDataService.initializeService(
|
|
87
|
+
(pError) =>
|
|
88
|
+
{
|
|
89
|
+
// Service initialized without starting the web server
|
|
90
|
+
// Start it manually when ready:
|
|
91
|
+
_Fable.Orator.startService(() => console.log('Server started'));
|
|
92
|
+
});
|
|
93
|
+
```
|
package/docs/cover.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Retold Data Service <small>2</small>
|
|
2
|
+
|
|
3
|
+
> All-in-one data service for the Retold ecosystem
|
|
4
|
+
|
|
5
|
+
- Schema-driven REST API generation from Stricture DDL models
|
|
6
|
+
- Automatic CRUD endpoints for every entity in your data model
|
|
7
|
+
- Pluggable storage providers: MySQL, SQLite, MSSQL, ALASQL
|
|
8
|
+
- Built on Fable, Meadow, and Orator with lifecycle hooks for customization
|
|
9
|
+
|
|
10
|
+
[GitHub](https://github.com/stevenvelozo/retold-data-service)
|
|
11
|
+
[Get Started](#retold-data-service)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# DAL Access
|
|
2
|
+
|
|
3
|
+
In addition to the REST endpoints, you can query your data directly through the Meadow DAL objects.
|
|
4
|
+
|
|
5
|
+
## Accessing DAL Objects
|
|
6
|
+
|
|
7
|
+
After initialization, every entity in your model has a DAL object accessible via `fable.DAL`:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// Read a single record
|
|
11
|
+
let tmpQuery = _Fable.DAL.Book.query.addFilter('IDBook', 42);
|
|
12
|
+
|
|
13
|
+
_Fable.DAL.Book.doRead(tmpQuery,
|
|
14
|
+
(pError, pQuery, pRecord) =>
|
|
15
|
+
{
|
|
16
|
+
console.log('Found:', pRecord.Title);
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Available Operations
|
|
21
|
+
|
|
22
|
+
| Method | Description |
|
|
23
|
+
|--------|-------------|
|
|
24
|
+
| `doCreate(query, callback)` | Insert a new record |
|
|
25
|
+
| `doRead(query, callback)` | Read a single record |
|
|
26
|
+
| `doReads(query, callback)` | Read multiple records |
|
|
27
|
+
| `doUpdate(query, callback)` | Update an existing record |
|
|
28
|
+
| `doDelete(query, callback)` | Soft-delete a record |
|
|
29
|
+
| `doUndelete(query, callback)` | Restore a soft-deleted record |
|
|
30
|
+
| `doCount(query, callback)` | Count matching records |
|
|
31
|
+
|
|
32
|
+
## Query Building
|
|
33
|
+
|
|
34
|
+
Each DAL has a `query` property (a FoxHound query instance) that you can configure with filters, sorting, and pagination:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// Read all Science Fiction books, newest first, page 1
|
|
38
|
+
let tmpQuery = _Fable.DAL.Book.query
|
|
39
|
+
.addFilter('Genre', 'Science Fiction')
|
|
40
|
+
.addSort({Column: 'PublicationYear', Direction: 'Descending'})
|
|
41
|
+
.setCap(25)
|
|
42
|
+
.setBegin(0);
|
|
43
|
+
|
|
44
|
+
_Fable.DAL.Book.doReads(tmpQuery,
|
|
45
|
+
(pError, pQuery, pRecords) =>
|
|
46
|
+
{
|
|
47
|
+
console.log(`Found ${pRecords.length} books`);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Creating Records
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
let tmpQuery = _Fable.DAL.Author.query
|
|
55
|
+
.addRecord({Name: 'Frank Herbert'});
|
|
56
|
+
|
|
57
|
+
_Fable.DAL.Author.doCreate(tmpQuery,
|
|
58
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
59
|
+
{
|
|
60
|
+
console.log('Created author with ID:', pRecord.IDAuthor);
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Counting Records
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
let tmpQuery = _Fable.DAL.Review.query;
|
|
68
|
+
|
|
69
|
+
_Fable.DAL.Review.doCount(tmpQuery,
|
|
70
|
+
(pError, pQuery, pCount) =>
|
|
71
|
+
{
|
|
72
|
+
console.log('Total reviews:', pCount);
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## When to Use DAL vs Endpoints
|
|
77
|
+
|
|
78
|
+
Use the **REST endpoints** when:
|
|
79
|
+
- Building a web UI that communicates over HTTP
|
|
80
|
+
- Exposing an API for external consumers
|
|
81
|
+
- You need session management and authorization
|
|
82
|
+
|
|
83
|
+
Use **DAL access** when:
|
|
84
|
+
- Running server-side business logic
|
|
85
|
+
- Performing batch operations
|
|
86
|
+
- Writing behavior injection hooks that need to query related entities
|
|
87
|
+
- Running background tasks or workers
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Endpoints
|
|
2
|
+
|
|
3
|
+
Once initialized, Retold Data Service automatically creates RESTful endpoints for every entity in your Stricture model.
|
|
4
|
+
|
|
5
|
+
## URL Pattern
|
|
6
|
+
|
|
7
|
+
All endpoints follow the Meadow Endpoints URL pattern:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
/{Version}/{Entity} — singular entity operations (Read, Create, Update, Delete)
|
|
11
|
+
/{Version}/{Entity}s — plural entity operations (Reads, Count)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The version defaults to `1.0`.
|
|
15
|
+
|
|
16
|
+
## Available Endpoints
|
|
17
|
+
|
|
18
|
+
For an entity named `Book`, these endpoints are created:
|
|
19
|
+
|
|
20
|
+
### Read (Single Record)
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
GET /1.0/Book/:IDRecord
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Returns a single record by primary key. Returns a 404 error object if not found.
|
|
27
|
+
|
|
28
|
+
### Read (Multiple Records)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
GET /1.0/Books
|
|
32
|
+
GET /1.0/Books/:Begin/:Cap
|
|
33
|
+
GET /1.0/Books/FilteredTo/:Filter
|
|
34
|
+
GET /1.0/Books/FilteredTo/:Filter/:Begin/:Cap
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Returns an array of records. Supports pagination via `Begin` and `Cap` parameters, and filtering via the Meadow filter stanza format.
|
|
38
|
+
|
|
39
|
+
### Create
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
POST /1.0/Book
|
|
43
|
+
Body: { "Title": "Dune", "Genre": "Science Fiction" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Creates a new record and returns the created record (with auto-generated ID and GUID).
|
|
47
|
+
|
|
48
|
+
### Update
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
PUT /1.0/Book
|
|
52
|
+
Body: { "IDBook": 1, "Title": "Dune (Updated)" }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Updates an existing record and returns the updated record. The request body must include the primary key.
|
|
56
|
+
|
|
57
|
+
### Delete (Soft Delete)
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
DELETE /1.0/Book/:IDRecord
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Soft-deletes a record (sets `Deleted = 1`). Returns the number of affected records.
|
|
64
|
+
|
|
65
|
+
### Count
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
GET /1.0/Books/Count
|
|
69
|
+
GET /1.0/Books/Count/FilteredTo/:Filter
|
|
70
|
+
GET /1.0/Books/Count/By/:ByField/:ByValue
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Returns the count of matching records as `{ "Count": N }`.
|
|
74
|
+
|
|
75
|
+
### Schema
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
GET /1.0/Book/Schema
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Returns the JSON Schema for the entity.
|
|
82
|
+
|
|
83
|
+
### New Default Record
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
GET /1.0/Book/Schema/New
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Returns a new default record with all fields set to their default values.
|
|
90
|
+
|
|
91
|
+
## Filter Stanza Format
|
|
92
|
+
|
|
93
|
+
Filters use the Meadow filter stanza format in the URL:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
FBV~Column~Operator~Value
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| Code | Meaning |
|
|
100
|
+
|------|---------|
|
|
101
|
+
| `FBV` | Filter By Value |
|
|
102
|
+
| `EQ` | Equals |
|
|
103
|
+
| `NE` | Not Equals |
|
|
104
|
+
| `GT` | Greater Than |
|
|
105
|
+
| `GE` | Greater Than or Equal |
|
|
106
|
+
| `LT` | Less Than |
|
|
107
|
+
| `LE` | Less Than or Equal |
|
|
108
|
+
| `LK` | LIKE |
|
|
109
|
+
|
|
110
|
+
### Examples
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
# Filter by exact genre
|
|
114
|
+
GET /1.0/Books/FilteredTo/FBV~Genre~EQ~Science Fiction
|
|
115
|
+
|
|
116
|
+
# Filter by title pattern (URL-encode the % as %25)
|
|
117
|
+
GET /1.0/Books/FilteredTo/FBV~Title~LK~%25Dune%25
|
|
118
|
+
|
|
119
|
+
# Count filtered records
|
|
120
|
+
GET /1.0/Books/Count/FilteredTo/FBV~Genre~EQ~Fantasy
|
|
121
|
+
```
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
7
|
+
<meta name="description" content="Documentation powered by pict-docuserve">
|
|
8
|
+
|
|
9
|
+
<title>Documentation</title>
|
|
10
|
+
|
|
11
|
+
<!-- Application Stylesheet -->
|
|
12
|
+
<link href="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/css/docuserve.css" rel="stylesheet">
|
|
13
|
+
<!-- KaTeX stylesheet for LaTeX equation rendering -->
|
|
14
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
|
|
15
|
+
<!-- PICT Dynamic View CSS Container -->
|
|
16
|
+
<style id="PICT-CSS"></style>
|
|
17
|
+
|
|
18
|
+
<!-- Load the PICT library from jsDelivr CDN -->
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/pict@1/dist/pict.min.js" type="text/javascript"></script>
|
|
20
|
+
<!-- Bootstrap the Application -->
|
|
21
|
+
<script type="text/javascript">
|
|
22
|
+
//<![CDATA[
|
|
23
|
+
Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(PictDocuserve, 2)});
|
|
24
|
+
//]]>
|
|
25
|
+
</script>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<!-- The root container for the Pict application -->
|
|
29
|
+
<div id="Docuserve-Application-Container"></div>
|
|
30
|
+
|
|
31
|
+
<!-- Mermaid diagram rendering -->
|
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
33
|
+
<script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
|
|
34
|
+
<!-- KaTeX for LaTeX equation rendering -->
|
|
35
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
|
|
36
|
+
<!-- Load the Docuserve PICT Application Bundle from jsDelivr CDN -->
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/pict-docuserve.min.js" type="text/javascript"></script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Initialization
|
|
2
|
+
|
|
3
|
+
Retold Data Service follows an ordered initialization sequence. Understanding this sequence is key to customizing the service behavior.
|
|
4
|
+
|
|
5
|
+
## Basic Initialization
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
const libFable = require('fable');
|
|
9
|
+
const libRetoldDataService = require('retold-data-service');
|
|
10
|
+
|
|
11
|
+
const _Fable = new libFable(settings);
|
|
12
|
+
|
|
13
|
+
// 1. Register the service type
|
|
14
|
+
_Fable.serviceManager.addServiceType('RetoldDataService', libRetoldDataService);
|
|
15
|
+
|
|
16
|
+
// 2. Instantiate the service with options
|
|
17
|
+
_Fable.serviceManager.instantiateServiceProvider('RetoldDataService', options);
|
|
18
|
+
|
|
19
|
+
// 3. Initialize the service
|
|
20
|
+
_Fable.RetoldDataService.initializeService(
|
|
21
|
+
(pError) =>
|
|
22
|
+
{
|
|
23
|
+
if (pError) return console.error('Init failed:', pError);
|
|
24
|
+
console.log('Ready!');
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Initialization Sequence
|
|
29
|
+
|
|
30
|
+
When `initializeService()` is called, the following steps run in order:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
1. onBeforeInitialize() ← your custom hook
|
|
34
|
+
2. Start Orator web server ← if AutoStartOrator is true
|
|
35
|
+
3. initializePersistenceEngine() ← loads the storage provider module
|
|
36
|
+
4. onInitialize() ← your custom hook
|
|
37
|
+
5. initializeDataEndpoints() ← loads model, creates DAL + endpoints
|
|
38
|
+
6. onAfterInitialize() ← your custom hook
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Step 5: initializeDataEndpoints()
|
|
42
|
+
|
|
43
|
+
This is the core step where the model is loaded and endpoints are created:
|
|
44
|
+
|
|
45
|
+
1. Load the compiled Stricture schema from `FullMeadowSchemaPath + FullMeadowSchemaFilename`
|
|
46
|
+
2. Extract the list of table names from `model.Tables`
|
|
47
|
+
3. For each table:
|
|
48
|
+
- Create a Meadow DAL from the table's `MeadowSchema` package
|
|
49
|
+
- Set the DAL's provider to the configured `StorageProvider`
|
|
50
|
+
- Create a Meadow Endpoints controller for the DAL
|
|
51
|
+
- Connect the endpoint routes to the Orator service server
|
|
52
|
+
|
|
53
|
+
## Double-Initialization Protection
|
|
54
|
+
|
|
55
|
+
Calling `initializeService()` a second time returns an error:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
_Fable.RetoldDataService.initializeService(
|
|
59
|
+
(pError) =>
|
|
60
|
+
{
|
|
61
|
+
// pError.message: "Retold Data Service Application is being
|
|
62
|
+
// initialized but has already been initialized..."
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Stopping the Service
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
_Fable.RetoldDataService.stopService(
|
|
70
|
+
(pError) =>
|
|
71
|
+
{
|
|
72
|
+
if (pError) return console.error('Stop failed:', pError);
|
|
73
|
+
console.log('Service stopped');
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Calling `stopService()` on an uninitialized service returns an error.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Lifecycle Hooks
|
|
2
|
+
|
|
3
|
+
Retold Data Service provides three lifecycle hooks that you can override to customize initialization behavior.
|
|
4
|
+
|
|
5
|
+
## Available Hooks
|
|
6
|
+
|
|
7
|
+
| Hook | Fires | Use Case |
|
|
8
|
+
|------|-------|----------|
|
|
9
|
+
| `onBeforeInitialize(fCallback)` | Before Orator starts and before the persistence engine loads | Environment checks, early configuration |
|
|
10
|
+
| `onInitialize(fCallback)` | After Orator starts and persistence engine loads, before data endpoints are created | Register additional services, run migrations |
|
|
11
|
+
| `onAfterInitialize(fCallback)` | After all data endpoints are created and routes are connected | Post-startup tasks, inject behaviors, warm caches |
|
|
12
|
+
|
|
13
|
+
## Overriding Hooks
|
|
14
|
+
|
|
15
|
+
You can override the hooks by subclassing `RetoldDataService`:
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libRetoldDataService = require('retold-data-service');
|
|
19
|
+
|
|
20
|
+
class MyDataService extends libRetoldDataService
|
|
21
|
+
{
|
|
22
|
+
onBeforeInitialize(fCallback)
|
|
23
|
+
{
|
|
24
|
+
this.fable.log.info('Custom pre-initialization...');
|
|
25
|
+
// Perform environment validation, load configs, etc.
|
|
26
|
+
return fCallback();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
onInitialize(fCallback)
|
|
30
|
+
{
|
|
31
|
+
this.fable.log.info('Custom initialization...');
|
|
32
|
+
// Run database migrations, register extra services, etc.
|
|
33
|
+
return fCallback();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onAfterInitialize(fCallback)
|
|
37
|
+
{
|
|
38
|
+
this.fable.log.info('Custom post-initialization...');
|
|
39
|
+
// Inject behaviors, seed data, start background tasks, etc.
|
|
40
|
+
this.fable.MeadowEndpoints.Book.controller.BehaviorInjection
|
|
41
|
+
.setBehavior('Read-PostOperation', myCustomHook);
|
|
42
|
+
return fCallback();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Hook Execution Order
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
initializeService() called
|
|
51
|
+
│
|
|
52
|
+
├── onBeforeInitialize()
|
|
53
|
+
│
|
|
54
|
+
├── Start Orator (if AutoStartOrator)
|
|
55
|
+
│
|
|
56
|
+
├── initializePersistenceEngine()
|
|
57
|
+
│
|
|
58
|
+
├── onInitialize()
|
|
59
|
+
│
|
|
60
|
+
├── initializeDataEndpoints()
|
|
61
|
+
│ ├── Load schema model
|
|
62
|
+
│ ├── Create DAL for each entity
|
|
63
|
+
│ ├── Create Endpoints for each entity
|
|
64
|
+
│ └── Connect routes to Orator
|
|
65
|
+
│
|
|
66
|
+
├── onAfterInitialize()
|
|
67
|
+
│
|
|
68
|
+
└── callback(error)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Error Handling
|
|
72
|
+
|
|
73
|
+
If any hook or step passes an error to its callback, the initialization chain stops and the error is passed to the `initializeService` callback:
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
onInitialize(fCallback)
|
|
77
|
+
{
|
|
78
|
+
// Signal an error to stop initialization
|
|
79
|
+
return fCallback(new Error('Database migration failed'));
|
|
80
|
+
}
|
|
81
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Schema Definition
|
|
2
|
+
|
|
3
|
+
Retold Data Service loads a compiled Stricture schema model to create its entities and endpoints.
|
|
4
|
+
|
|
5
|
+
## The Stricture DDL
|
|
6
|
+
|
|
7
|
+
Schemas are defined using Stricture's DDL syntax, then compiled into a JSON model that Retold Data Service understands.
|
|
8
|
+
|
|
9
|
+
### DDL Example
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
!Book
|
|
13
|
+
@IDBook
|
|
14
|
+
%GUIDBook
|
|
15
|
+
&CreateDate
|
|
16
|
+
#CreatingIDUser
|
|
17
|
+
&UpdateDate
|
|
18
|
+
#UpdatingIDUser
|
|
19
|
+
^Deleted
|
|
20
|
+
&DeleteDate
|
|
21
|
+
#DeletingIDUser
|
|
22
|
+
$Title 200
|
|
23
|
+
$Type 32
|
|
24
|
+
$Genre 128
|
|
25
|
+
$ISBN 64
|
|
26
|
+
$Language 12
|
|
27
|
+
#PublicationYear
|
|
28
|
+
|
|
29
|
+
!Author
|
|
30
|
+
@IDAuthor
|
|
31
|
+
%GUIDAuthor
|
|
32
|
+
&CreateDate
|
|
33
|
+
#CreatingIDUser
|
|
34
|
+
&UpdateDate
|
|
35
|
+
#UpdatingIDUser
|
|
36
|
+
^Deleted
|
|
37
|
+
&DeleteDate
|
|
38
|
+
#DeletingIDUser
|
|
39
|
+
$Name 200
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### DDL Symbols
|
|
43
|
+
|
|
44
|
+
| Symbol | Meaning | Meadow Type |
|
|
45
|
+
|--------|---------|-------------|
|
|
46
|
+
| `!` | Table/entity name | — |
|
|
47
|
+
| `@` | Auto-increment ID | `AutoIdentity` |
|
|
48
|
+
| `%` | Auto GUID | `AutoGUID` |
|
|
49
|
+
| `&` | DateTime column | `CreateDate`, `UpdateDate`, `DeleteDate`, or `DateTime` |
|
|
50
|
+
| `#` | Numeric column | `Integer`, `CreateIDUser`, `UpdateIDUser`, `DeleteIDUser` |
|
|
51
|
+
| `$` | String column (with size) | `String` |
|
|
52
|
+
| `^` | Boolean column | `Deleted` or `Boolean` |
|
|
53
|
+
| `.` | Decimal column (with precision) | `Decimal` |
|
|
54
|
+
| `*` | Text column | `String` (large) |
|
|
55
|
+
|
|
56
|
+
## Compiling the Model
|
|
57
|
+
|
|
58
|
+
Use Stricture to compile the DDL into JSON:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx stricture -i model/ddl/BookStore.ddl
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This produces a `MeadowModel-Extended.json` file containing the full model with:
|
|
65
|
+
- Column definitions per table
|
|
66
|
+
- Meadow schema per table (with `AutoIdentity`, `AutoGUID`, `CreateDate`, etc.)
|
|
67
|
+
- JSON Schema per table
|
|
68
|
+
- Default objects per table
|
|
69
|
+
- Authorization rules per table
|
|
70
|
+
|
|
71
|
+
## Model JSON Structure
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"Tables": {
|
|
76
|
+
"Book": {
|
|
77
|
+
"TableName": "Book",
|
|
78
|
+
"Columns": [...],
|
|
79
|
+
"MeadowSchema": {
|
|
80
|
+
"Scope": "Book",
|
|
81
|
+
"DefaultIdentifier": "IDBook",
|
|
82
|
+
"Schema": [
|
|
83
|
+
{"Column": "IDBook", "Type": "AutoIdentity"},
|
|
84
|
+
{"Column": "GUIDBook", "Type": "AutoGUID"},
|
|
85
|
+
{"Column": "Title", "Type": "String", "Size": "200"},
|
|
86
|
+
...
|
|
87
|
+
],
|
|
88
|
+
"DefaultObject": {
|
|
89
|
+
"IDBook": 0,
|
|
90
|
+
"Title": "",
|
|
91
|
+
...
|
|
92
|
+
},
|
|
93
|
+
"Authorization": { ... }
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"Author": { ... }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Build Script
|
|
102
|
+
|
|
103
|
+
The test model includes a build script for compiling the DDL:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm run build-test-model
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This runs `cd test && npx stricture -i model/ddl/BookStore.ddl` to regenerate the compiled model from the DDL.
|
|
110
|
+
|
|
111
|
+
## Schema Types Reference
|
|
112
|
+
|
|
113
|
+
| Type | Description | Auto-Managed |
|
|
114
|
+
|------|-------------|-------------|
|
|
115
|
+
| `AutoIdentity` | Auto-increment primary key | On Create (NULL/omit) |
|
|
116
|
+
| `AutoGUID` | Unique identifier | On Create (UUID generated) |
|
|
117
|
+
| `CreateDate` | Creation timestamp | On Create |
|
|
118
|
+
| `CreateIDUser` | Creator user ID | On Create |
|
|
119
|
+
| `UpdateDate` | Last update timestamp | On Create and Update |
|
|
120
|
+
| `UpdateIDUser` | Last updater user ID | On Create and Update |
|
|
121
|
+
| `Deleted` | Soft-delete flag | On Delete (set to 1) |
|
|
122
|
+
| `DeleteDate` | Deletion timestamp | On Delete |
|
|
123
|
+
| `DeleteIDUser` | Deleter user ID | On Delete |
|
|
124
|
+
| `String` | Text data | — |
|
|
125
|
+
| `Integer` | Numeric data | — |
|
|
126
|
+
| `Decimal` | Decimal data | — |
|
|
127
|
+
| `Boolean` | Boolean data | — |
|
|
128
|
+
| `DateTime` | Date/time data | — |
|