1 | # Cradle - A Schema Pipeline [![Build Status](https://gatewayapps.visualstudio.com/Cradle/_apis/build/status/Cradle%20CI)](https://gatewayapps.visualstudio.com/Cradle/_build/latest?definitionId=34)
|
2 | ## What is Cradle?
|
3 | Cradle is a tool for loading data models from one place and emitting it to another. This can be leveraged to eliminate the manual creation of redundant and tedious code within your project/application. We provide the ability to create specs that can, in turn, be executed against emitters with the output resulting in the code you wish to use.
|
4 |
|
5 | [Getting Started](#getting-started)
|
6 | [An Example Cradle Spec](#an-example-cradle-spec)
|
7 | [Cradle Schema](#cradle-schema)
|
8 | [The Cradle CLI](#the-cradle-cli)
|
9 | [Configuration](#configuration)
|
10 | [Loaders](#loaders)
|
11 | [Emitters](#emitters)
|
12 |
|
13 |
|
14 | ## Getting Started
|
15 | To get started, first install Cradle in your local project folder:
|
16 | `npm i --save-dev @gatewayapps/cradle`
|
17 |
|
18 | ## The Cradle Flow
|
19 | Cradle was built to load a schema from any source via CradleLoaders and emit to any destination via CradleEmitters.
|
20 |
|
21 | When you execute ```npx cradle emit```, it will load your schema, then send it to each of your configured emitters. We have developed a few emitters for you to use (@gatewayapps/cradle-template-emitter and @gatewayapps/cradle-react-emitter), but the API is simple to understand and implement.
|
22 |
|
23 | ## Cradle Loader API
|
24 | If you want to develop a custom Cradle Loader, (for instance, loading from Postgres or Mongo), all you have to do is implement ICradleLoader in a npm module.
|
25 | ```
|
26 | prepareLoader: (options: {[key: string]: any}, console: IConsole) => Promise < void >
|
27 | readModelNames: () => Promise < string[] >
|
28 | readModelPropertyNames: (modelName: string) => Promise < string[] >
|
29 | readModelPropertyType: (modelName: string, propertyName: string) => Promise < PropertyType >
|
30 | readModelReferenceNames: (modelName: string) => Promise < string[] >
|
31 | readModelReferenceType: (modelName: string, referenceName: string) => Promise < ModelReference >
|
32 | readModelMetadata: (modelName: string) => Promise < object >
|
33 | finalizeSchema: (schema: CradleSchema) => Promise < CradleSchema >
|
34 | loadSchema: () => Promise<CradleSchema>
|
35 | ```
|
36 |
|
37 | You can reference @gatewayapps/cradle-sql-loader for a functioning loader
|
38 |
|
39 | ## Cradle Emitter API
|
40 |
|
41 | You can also write a custom emitter. Suppose you wanted to write your schema to a database, you could implement a sql-emitter by implementing the ICradleEmitter interface.
|
42 | ```
|
43 | prepareEmitter(options: IEmitterOptions, console: IConsole)
|
44 | emitSchema(schema: CradleSchema)
|
45 | ```
|
46 |
|
47 | You can reference @gatewayapps/cradle-template-emitter or @gatewayapps/cradle-react-emitter to see a functioning emitter.
|
48 |
|
49 | ## An Example Cradle Spec
|
50 | Cradle provides a custom loader and emitter out of the box called spec. The cradle spec is something we developed for easily modeling our data in an agnostic way. Here's a sample
|
51 | ```
|
52 | Film:
|
53 | properties:
|
54 | id: integer primary auto(1,1)
|
55 | name: string(100)
|
56 | totalBoxOffice: decimal min(0)
|
57 | releaseDate: datetime
|
58 | isDeleted: boolean default(false) delete
|
59 | actors:
|
60 | isArray: true
|
61 | modelRef: Actor
|
62 | operations:
|
63 | createFilm:
|
64 | returns: Film
|
65 | arguments:
|
66 | name:
|
67 |
|
68 | Actor:
|
69 | properties:
|
70 | id: integer primary auto(1,1)
|
71 | firstName: string(100)
|
72 | lastName: string(100)
|
73 | dateOfBirth: datetime
|
74 | ```
|
75 | To learn more about defining a cradle spec, see the wiki page [here](https://github.com/gatewayapps/cradle/wiki/The-Cradle-Spec)
|
76 |
|
77 | ## Cradle Schema
|
78 | Let's see what a cradle schema looks like as a JSON object:
|
79 | ```
|
80 | {
|
81 | "Models": [
|
82 | {
|
83 | "Name": "Film",
|
84 | "Properties": {
|
85 | "id": {
|
86 | "TypeName": "Integer",
|
87 | "IsPrimaryKey": true,
|
88 | "AllowNull": false,
|
89 | "Unique": false,
|
90 | "Autogenerate": {
|
91 | "Seed": 1,
|
92 | "Increment": 1
|
93 | }
|
94 | },
|
95 | "name": {
|
96 | "TypeName": "String",
|
97 | "IsPrimaryKey": false,
|
98 | "AllowNull": false,
|
99 | "Unique": false,
|
100 | "MaximumLength": 100
|
101 | },
|
102 | "totalBoxOffice": {
|
103 | "TypeName": "Decimal",
|
104 | "IsPrimaryKey": false,
|
105 | "AllowNull": false,
|
106 | "Unique": false,
|
107 | "MinimumValue": 0,
|
108 | "Precision": 18,
|
109 | "Scale": 2
|
110 | },
|
111 | "releaseDate": {
|
112 | "TypeName": "DateTime",
|
113 | "IsPrimaryKey": false,
|
114 | "AllowNull": false,
|
115 | "Unique": false
|
116 | },
|
117 | "isDeleted": {
|
118 | "TypeName": "Boolean",
|
119 | "IsPrimaryKey": false,
|
120 | "AllowNull": false,
|
121 | "DefaultValue": false,
|
122 | "Unique": false
|
123 | },
|
124 | "actors": {
|
125 | "TypeName": "Array",
|
126 | "IsPrimaryKey": false,
|
127 | "AllowNull": false,
|
128 | "DefaultValue": [],
|
129 | "Unique": false,
|
130 | "MemberType": {
|
131 | "TypeName": "ModelReference",
|
132 | "IsPrimaryKey": false,
|
133 | "AllowNull": true,
|
134 | "DefaultValue": null,
|
135 | "Unique": false,
|
136 | "ModelName": "Actor",
|
137 | "ModelType": {
|
138 | "TypeName": "Object",
|
139 | "IsPrimaryKey": false,
|
140 | "AllowNull": false,
|
141 | "Unique": false,
|
142 | "Members": {
|
143 | "id": {
|
144 | "TypeName": "Integer",
|
145 | "IsPrimaryKey": true,
|
146 | "AllowNull": false,
|
147 | "Unique": false,
|
148 | "Autogenerate": {
|
149 | "Seed": 1,
|
150 | "Increment": 1
|
151 | }
|
152 | },
|
153 | "firstName": {
|
154 | "TypeName": "String",
|
155 | "IsPrimaryKey": false,
|
156 | "AllowNull": false,
|
157 | "Unique": false,
|
158 | "MaximumLength": 100
|
159 | },
|
160 | "lastName": {
|
161 | "TypeName": "String",
|
162 | "IsPrimaryKey": false,
|
163 | "AllowNull": false,
|
164 | "Unique": false,
|
165 | "MaximumLength": 100
|
166 | },
|
167 | "dateOfBirth": {
|
168 | "TypeName": "DateTime",
|
169 | "IsPrimaryKey": false,
|
170 | "AllowNull": false,
|
171 | "Unique": false
|
172 | }
|
173 | }
|
174 | }
|
175 | }
|
176 | }
|
177 | },
|
178 | "References": {}
|
179 | },
|
180 | {
|
181 | "Name": "Actor",
|
182 | "Properties": {
|
183 | "id": {
|
184 | "TypeName": "Integer",
|
185 | "IsPrimaryKey": true,
|
186 | "AllowNull": false,
|
187 | "Unique": false,
|
188 | "Autogenerate": {
|
189 | "Seed": 1,
|
190 | "Increment": 1
|
191 | }
|
192 | },
|
193 | "firstName": {
|
194 | "TypeName": "String",
|
195 | "IsPrimaryKey": false,
|
196 | "AllowNull": false,
|
197 | "Unique": false,
|
198 | "MaximumLength": 100
|
199 | },
|
200 | "lastName": {
|
201 | "TypeName": "String",
|
202 | "IsPrimaryKey": false,
|
203 | "AllowNull": false,
|
204 | "Unique": false,
|
205 | "MaximumLength": 100
|
206 | },
|
207 | "dateOfBirth": {
|
208 | "TypeName": "DateTime",
|
209 | "IsPrimaryKey": false,
|
210 | "AllowNull": false,
|
211 | "Unique": false
|
212 | }
|
213 | },
|
214 | "References": {}
|
215 | }
|
216 | ]
|
217 | }
|
218 | ```
|
219 | - `Models`: is an array of the models defined in the cradle spec file(s)
|
220 | - `TypeName`: is the cradle data type
|
221 | - `AllowNull`: is true if the `?` notation was used to define the property
|
222 | - `Unique`: is true if the `unique` keyword was used to define the property
|
223 | - If the property is an array, then there is a `MemberType` property which contains the values and data type of the array
|
224 |
|
225 | ## The Cradle CLI
|
226 | Cradle provides a CLI with a few different commands:
|
227 |
|
228 | ### verify
|
229 | Use the `verify` command to ensure that any given spec file is valid:
|
230 | `npx cradle verify -c [path to cradle config]`
|
231 |
|
232 | This command will output the cradle spec in the terminal or command window in which the command was executed. Any warnings or errors will also be output.
|
233 |
|
234 | ### emit
|
235 | Use the `emit` command to run the specified emitter:
|
236 | `npx cradle emit -c [path to cradle config] -e spec`
|
237 |
|
238 | This command will run the specified emitters(see the Configuration section). Use the `-e` argument to specify a particular emitter or omit it to run all configured emitters.
|
239 |
|
240 | ## Configuration
|
241 | Cradle uses a JavaScript based configuration file in order to execute loading the spec and executing emitters. Below is an example configuration file:
|
242 |
|
243 | ```
|
244 | const cradle = require('@gatewayapps/cradle')
|
245 | const path = require('path')
|
246 |
|
247 | const loaderOptions = new cradle.LoaderOptions('spec', {
|
248 | source: './examples/specs/cradle-base.yaml'
|
249 | }, console)
|
250 |
|
251 | const emitterOpts = [
|
252 | new cradle.EmitterOptions('schemaTest', '@gatewayapps/cradle-template-emitter', {
|
253 | sourcePath: './examples/templates/schemaTest.handlebars',
|
254 | outputPath: './examples/server/test/schemaTest.ts',
|
255 | overwriteExisting: true,
|
256 | mode: 'schema',
|
257 | shouldEmit: (model) => {
|
258 | return model.Meta !== undefined && model.Meta.topLevel
|
259 | }
|
260 | }, console),
|
261 | new cradle.EmitterOptions('serverModels', '@gatewayapps/cradle-template-emitter', {
|
262 | sourcePath: './examples/templates/serverModel.handlebars',
|
263 | outputPath: './examples/server/models/{{Name}}.ts',
|
264 | overwriteExisting: true,
|
265 | languageType: 'mongoose',
|
266 | shouldEmit: (model) => {
|
267 | return model.Meta !== undefined && model.Meta.topLevel
|
268 | }
|
269 | }, console)
|
270 | ]
|
271 |
|
272 | module.exports = new cradle.CradleConfig(loaderOptions, emitterOpts)
|
273 |
|
274 | ```
|
275 |
|
276 | Let's break down the configuration file in more detail:
|
277 | ```
|
278 | const loaderOptions = new cradle.LoaderOptions('spec', {
|
279 | source: './examples/specs/cradle-base.yaml'
|
280 | }, console)
|
281 | ```
|
282 | This section configures the cradle spec loader. This is required in order to read the cradle spec files. The first option is the type of loader(`spec`), the second option is the location of the cradle spec file.
|
283 |
|
284 | ```
|
285 | const emitterOpts = [
|
286 | new cradle.EmitterOptions('schemaTest', '@gatewayapps/cradle-template-emitter', {
|
287 | sourcePath: './examples/templates/schemaTest.handlebars',
|
288 | outputPath: './examples/server/test/schemaTest.ts',
|
289 | overwriteExisting: true,
|
290 | mode: 'schema',
|
291 | shouldEmit: (model) => {
|
292 | return model.Meta !== undefined && model.Meta.topLevel
|
293 | }
|
294 | }, console),
|
295 | new cradle.EmitterOptions('serverModels', '@gatewayapps/cradle-template-emitter', {
|
296 | sourcePath: './examples/templates/serverModel.handlebars',
|
297 | outputPath: './examples/server/models/{{Name}}.ts',
|
298 | overwriteExisting: true,
|
299 | languageType: 'mongoose',
|
300 | shouldEmit: (model) => {
|
301 | return model.Meta !== undefined && model.Meta.topLevel
|
302 | }
|
303 | }, console)
|
304 | ]
|
305 | ```
|
306 | This section is configuring emitters. These two examples uses the [cradle template emitter](https://github.com/gatewayapps/cradle-template-emitter) in order to generate code. More information about these arguments can be found in the ReadMe for that particular library.
|
307 |
|
308 | ## Loaders
|
309 | - Cradle Spec Loader: This is included in cradle and drives the loading of the cradle spec files
|
310 | - [MSSQL Loader](cradle-mssql-loader): This loader can be used to read the database schema from a MSSQL database and generate a cradle spec
|
311 |
|
312 | ## Emitters
|
313 | - Cradle Spec Emitter: This is included in cradle and can be used to output a generated spec file
|
314 | - [Cradle Template Emitter](https://github.com/gatewayapps/cradle-template-emitter): This emitter leverages Handlebars templating in order to generate pretty much any code desired
|
315 | - [Cradle React Emitter](https://github.com/gatewayapps/cradle-react-emitter): This emitter generates basic React components for model properties. Rather than starting a UI from scratch, this emitter provides a starting point with functional components that can then be tweaked/altered.
|
316 |
|
\ | No newline at end of file |