@acodeninja/persist 3.0.0-next.8 → 3.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/README.md +60 -4
- package/docs/code-quirks.md +14 -14
- package/docs/defining-models.md +61 -0
- package/docs/http.openapi.yml +138 -0
- package/docs/{model-property-types.md → model-properties.md} +76 -43
- package/docs/models-as-properties.md +46 -46
- package/docs/search-queries.md +11 -13
- package/docs/storage-engines.md +19 -35
- package/docs/structured-queries.md +59 -48
- package/docs/transactions.md +6 -7
- package/exports/storage/http.js +3 -0
- package/exports/storage/s3.js +3 -0
- package/jest.config.cjs +8 -12
- package/package.json +2 -2
- package/src/Connection.js +750 -0
- package/src/Persist.js +29 -30
- package/src/Schema.js +175 -0
- package/src/{Query.js → data/FindIndex.js} +40 -24
- package/src/{type → data}/Model.js +95 -55
- package/src/data/Property.js +21 -0
- package/src/data/SearchIndex.js +106 -0
- package/src/{type/complex → data/properties}/ArrayType.js +5 -3
- package/src/{type/simple → data/properties}/BooleanType.js +3 -3
- package/src/{type/complex → data/properties}/CustomType.js +5 -5
- package/src/{type/simple → data/properties}/DateType.js +4 -4
- package/src/{type/simple → data/properties}/NumberType.js +3 -3
- package/src/{type/resolved → data/properties}/ResolvedType.js +3 -2
- package/src/{type/resolved → data/properties}/SlugType.js +1 -1
- package/src/{type/simple → data/properties}/StringType.js +3 -3
- package/src/{type → data/properties}/Type.js +13 -3
- package/src/engine/storage/HTTPStorageEngine.js +149 -253
- package/src/engine/storage/S3StorageEngine.js +108 -195
- package/src/engine/storage/StorageEngine.js +131 -549
- package/exports/engine/storage/file.js +0 -3
- package/exports/engine/storage/http.js +0 -3
- package/exports/engine/storage/s3.js +0 -3
- package/src/SchemaCompiler.js +0 -196
- package/src/Transactions.js +0 -145
- package/src/engine/StorageEngine.js +0 -350
- package/src/engine/storage/FileStorageEngine.js +0 -213
- package/src/type/index.js +0 -32
package/README.md
CHANGED
@@ -9,18 +9,74 @@ A JSON based data modelling and persistence library with alternate storage mecha
|
|
9
9
|
|
10
10
|
[](https://app.deepsource.com/gh/acodeninja/persist/)
|
11
11
|
[](https://app.deepsource.com/gh/acodeninja/persist/)
|
12
|
+

|
12
13
|
|
13
14
|
## Features
|
14
15
|
|
15
16
|
- Data modelling with relationships
|
16
17
|
- Data validation
|
17
|
-
- Data
|
18
|
-
-
|
19
|
-
|
18
|
+
- Data queries and fuzzy search
|
19
|
+
- Store data using AWS S3 or HTTP APIs
|
20
|
+
|
21
|
+
## Terms
|
22
|
+
|
23
|
+
`model`
|
24
|
+
: Defines the shape of an entity's data within your application.
|
25
|
+
|
26
|
+
`property`
|
27
|
+
: Analogous to a data type, allows defining the type of properties associated with a `model`.
|
28
|
+
|
29
|
+
`connection`
|
30
|
+
: Abstraction layer that establishes a connection to the configured storage engine and supports CRUD and query functionality.
|
31
|
+
|
32
|
+
`engine`
|
33
|
+
: An engine allows a connection to send instructions to a given service, it may support anything from a RESTFul HTTP API to a cloud service like S3 or DynamoDB.
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Models
|
38
|
+
|
39
|
+
```javascript
|
40
|
+
import Persist from '@acodeninja/persist';
|
41
|
+
|
42
|
+
class Person extends Persist.Model {
|
43
|
+
static {
|
44
|
+
Person.name = Persist.Property.String.required;
|
45
|
+
Person.dateOfBirth = Persist.Property.Date.required;
|
46
|
+
Person.height = Persist.Property.Number.required;
|
47
|
+
Person.isStudent = Persist.Property.Boolean.required;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
```
|
51
|
+
|
52
|
+
### Storage
|
53
|
+
|
54
|
+
```javascript
|
55
|
+
import Persist from '@acodeninja/persist';
|
56
|
+
import {S3Client} from "@aws-sdk/client-s3";
|
57
|
+
import S3StorageEngine from '@acodeninja/persist/storage/s3';
|
58
|
+
|
59
|
+
const engine = new S3StorageEngine({
|
60
|
+
bucket: 'person-storage',
|
61
|
+
client: new S3Client(),
|
62
|
+
});
|
63
|
+
|
64
|
+
const connection = Persist.registerConnection('people', engine, [Person]);
|
65
|
+
|
66
|
+
const person = new Person({
|
67
|
+
name: 'Joe Bloggs',
|
68
|
+
dateOfBirth: new Date('1993-04-02T00:00:00.000Z'),
|
69
|
+
height: 1.85,
|
70
|
+
isStudent: true,
|
71
|
+
});
|
72
|
+
|
73
|
+
await connection.put(person);
|
74
|
+
```
|
20
75
|
|
21
76
|
## Find out more
|
22
77
|
|
23
|
-
- [
|
78
|
+
- [Defining Models](./docs/defining-models.md)
|
79
|
+
- [Model Property Types](./docs/model-properties.md)
|
24
80
|
- [Models as Properties](./docs/models-as-properties.md)
|
25
81
|
- [Structured Queries](./docs/structured-queries.md)
|
26
82
|
- [Search Queries](./docs/search-queries.md)
|
package/docs/code-quirks.md
CHANGED
@@ -9,15 +9,15 @@ When you bundle or minify JavaScript code for production, class names are often
|
|
9
9
|
To avoid this problem, you have two options:
|
10
10
|
|
11
11
|
1. Disable class name mangling in your minifier.
|
12
|
-
2. Use `this.
|
12
|
+
2. Use `this.withName(name)` to manually specify the model's name.
|
13
13
|
|
14
14
|
```javascript
|
15
15
|
import Persist from "@acodeninja/persist";
|
16
16
|
|
17
|
-
export class Person extends Persist.
|
17
|
+
export class Person extends Persist.Model {
|
18
18
|
static {
|
19
|
-
|
20
|
-
|
19
|
+
Person.withName('Person');
|
20
|
+
Person.name = Persist.Property.String.required;
|
21
21
|
}
|
22
22
|
}
|
23
23
|
```
|
@@ -37,17 +37,17 @@ To avoid these errors, always define model relationships using arrow functions.
|
|
37
37
|
```javascript
|
38
38
|
import Persist from "@acodeninja/persist";
|
39
39
|
|
40
|
-
export class Person extends Persist.
|
40
|
+
export class Person extends Persist.Model {
|
41
41
|
static {
|
42
|
-
|
42
|
+
Person.address = () => Address;
|
43
43
|
}
|
44
44
|
}
|
45
45
|
|
46
|
-
export class Address extends Persist.
|
46
|
+
export class Address extends Persist.Model {
|
47
47
|
static {
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
Address.person = () => Person;
|
49
|
+
Address.address = Persist.Property.String.required;
|
50
|
+
Address.postcode = Persist.Property.String.required;
|
51
51
|
}
|
52
52
|
}
|
53
53
|
```
|
@@ -60,12 +60,12 @@ When implementing thee `HTTP` engine for code that runs in the web browser, you
|
|
60
60
|
|
61
61
|
```javascript
|
62
62
|
import Persist from "@acodeninja/persist";
|
63
|
-
import HTTPStorageEngine from "@acodeninja/persist/
|
63
|
+
import HTTPStorageEngine from "@acodeninja/persist/storage/http";
|
64
64
|
|
65
|
-
Persist.
|
66
|
-
|
65
|
+
Persist.registerConnection('people', new HTTPStorageEngine({
|
66
|
+
baseUrl: 'https://api.example.com',
|
67
67
|
fetch: fetch.bind(window),
|
68
|
-
});
|
68
|
+
}));
|
69
69
|
```
|
70
70
|
|
71
71
|
This will ensure that `fetch` can access the window context which is required for it to function.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Defining Models
|
2
|
+
|
3
|
+
Persist allows defining models as JavaScript classes and uses static attributes for the properties of each model, this allows for validation and type coercion when saving and retrieving data.
|
4
|
+
|
5
|
+
## Defining a Model
|
6
|
+
|
7
|
+
The most basic model is a JavaScript class that extends `Persist.Model`.
|
8
|
+
|
9
|
+
```javascript
|
10
|
+
class BasicModel extends Persist.Model {
|
11
|
+
}
|
12
|
+
```
|
13
|
+
|
14
|
+
A model will always have an `id` property that is randomly generated using [ULID](https://github.com/ulid/spec).
|
15
|
+
|
16
|
+
```javascript
|
17
|
+
const basic = new BasicModel();
|
18
|
+
|
19
|
+
console.log(basic.id) // BasicModel/01ARZ3NDEKTSV4RRFFQ69G5FAV
|
20
|
+
```
|
21
|
+
|
22
|
+
### Adding properties
|
23
|
+
|
24
|
+
Model properties can be added to a model using `Persist.Property.*`.
|
25
|
+
|
26
|
+
```javascript
|
27
|
+
import Persist from '@acodeninja/persist';
|
28
|
+
|
29
|
+
class Person extends Persist.Model {
|
30
|
+
static {
|
31
|
+
Person.name = Persist.Property.String.required;
|
32
|
+
Person.dateOfBirth = Persist.Property.Date.required;
|
33
|
+
Person.height = Persist.Property.Number.required;
|
34
|
+
Person.isStudent = Persist.Property.Boolean.required;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
For a full list of available property types see [Model Property Types](./model-properties).
|
40
|
+
|
41
|
+
### Linking Models
|
42
|
+
|
43
|
+
Models can be linked to other models by declaring them as properties.
|
44
|
+
|
45
|
+
```javascript
|
46
|
+
class Address extends Persist.Model {
|
47
|
+
static {
|
48
|
+
Address.address = Persist.Property.String.required;
|
49
|
+
Address.postcode = Persist.Property.String.required;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
class Person extends Persist.Model {
|
54
|
+
static {
|
55
|
+
Person.name = Persist.Property.String.required;
|
56
|
+
Person.address = Address;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
```
|
60
|
+
|
61
|
+
For a look at the ways that models can be linked check out [Models as Properties](./models-as-properties.md).
|
@@ -0,0 +1,138 @@
|
|
1
|
+
openapi: 3.1.0
|
2
|
+
|
3
|
+
info:
|
4
|
+
title: Persist Generic HTTP API
|
5
|
+
version: 1.0.0
|
6
|
+
|
7
|
+
paths:
|
8
|
+
/{model}/{id}:
|
9
|
+
get:
|
10
|
+
summary: Retrieve an existing model instance.
|
11
|
+
parameters:
|
12
|
+
- in: path
|
13
|
+
name: model
|
14
|
+
schema:
|
15
|
+
type: string
|
16
|
+
required: true
|
17
|
+
description: Name of the model
|
18
|
+
- in: path
|
19
|
+
name: id
|
20
|
+
schema:
|
21
|
+
type: string
|
22
|
+
required: true
|
23
|
+
description: Unique identifier for a model
|
24
|
+
responses:
|
25
|
+
'200':
|
26
|
+
description: Successful update operation
|
27
|
+
content:
|
28
|
+
application/json:
|
29
|
+
schema:
|
30
|
+
$ref: '#/components/schemas/Model'
|
31
|
+
'404':
|
32
|
+
description: Model not found
|
33
|
+
put:
|
34
|
+
summary: Create or update an existing model instance.
|
35
|
+
parameters:
|
36
|
+
- in: path
|
37
|
+
name: model
|
38
|
+
schema:
|
39
|
+
type: string
|
40
|
+
required: true
|
41
|
+
description: Name of the model
|
42
|
+
- in: path
|
43
|
+
name: id
|
44
|
+
schema:
|
45
|
+
type: string
|
46
|
+
required: true
|
47
|
+
description: Unique identifier for a model
|
48
|
+
requestBody:
|
49
|
+
description: Update or create a model instance.
|
50
|
+
content:
|
51
|
+
application/json:
|
52
|
+
schema:
|
53
|
+
$ref: '#/components/schemas/Model'
|
54
|
+
required: true
|
55
|
+
responses:
|
56
|
+
'200':
|
57
|
+
description: Successful update operation
|
58
|
+
'201':
|
59
|
+
description: Successful create operation
|
60
|
+
'422':
|
61
|
+
description: Validation exception
|
62
|
+
delete:
|
63
|
+
summary: Delete an instance of a model
|
64
|
+
parameters:
|
65
|
+
- in: path
|
66
|
+
name: model
|
67
|
+
schema:
|
68
|
+
type: string
|
69
|
+
required: true
|
70
|
+
description: Name of the model
|
71
|
+
- in: path
|
72
|
+
name: id
|
73
|
+
schema:
|
74
|
+
type: string
|
75
|
+
required: true
|
76
|
+
description: Unique identifier for a model
|
77
|
+
responses:
|
78
|
+
'204':
|
79
|
+
description: Successful operation
|
80
|
+
'422':
|
81
|
+
description: Validation exception
|
82
|
+
/{model}:
|
83
|
+
get:
|
84
|
+
summary: Retrieve an index of the current models.
|
85
|
+
parameters:
|
86
|
+
- in: path
|
87
|
+
name: model
|
88
|
+
schema:
|
89
|
+
type: string
|
90
|
+
required: true
|
91
|
+
description: Name of the model
|
92
|
+
responses:
|
93
|
+
'200':
|
94
|
+
description: Successful update operation
|
95
|
+
content:
|
96
|
+
application/json:
|
97
|
+
schema:
|
98
|
+
$ref: '#/components/schemas/ModelIndex'
|
99
|
+
'404':
|
100
|
+
description: Model not found
|
101
|
+
/{model}/search:
|
102
|
+
get:
|
103
|
+
summary: Retrieve a search index of the current models.
|
104
|
+
parameters:
|
105
|
+
- in: path
|
106
|
+
name: model
|
107
|
+
schema:
|
108
|
+
type: string
|
109
|
+
required: true
|
110
|
+
description: Name of the model
|
111
|
+
responses:
|
112
|
+
'200':
|
113
|
+
description: Successful update operation
|
114
|
+
content:
|
115
|
+
application/json:
|
116
|
+
schema:
|
117
|
+
$ref: '#/components/schemas/ModelIndex'
|
118
|
+
'404':
|
119
|
+
description: Model not found
|
120
|
+
|
121
|
+
components:
|
122
|
+
schemas:
|
123
|
+
Model:
|
124
|
+
required:
|
125
|
+
- id
|
126
|
+
type: object
|
127
|
+
additionalProperties: true
|
128
|
+
properties:
|
129
|
+
id:
|
130
|
+
type: string
|
131
|
+
ModelIndex:
|
132
|
+
properties:
|
133
|
+
"{model}/{id}":
|
134
|
+
type: object
|
135
|
+
additionalProperties: true
|
136
|
+
properties:
|
137
|
+
id:
|
138
|
+
type: string
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Model
|
1
|
+
# Model Properties
|
2
2
|
|
3
3
|
Persist uses a type definition for the properties of each model, this allows for validation and type coercion when saving and retrieving data.
|
4
4
|
|
@@ -11,103 +11,105 @@ Properties can be defined on a model by setting static properties to the value o
|
|
11
11
|
```javascript
|
12
12
|
import Persist from '@acodeninja/persist';
|
13
13
|
|
14
|
-
class Person extends Persist.
|
14
|
+
class Person extends Persist.Model {
|
15
15
|
static {
|
16
|
-
|
17
|
-
|
16
|
+
Person.firstName = Persist.Property.String;
|
17
|
+
Person.lastName = Persist.Property.String;
|
18
18
|
}
|
19
19
|
}
|
20
20
|
```
|
21
21
|
|
22
|
-
## Simple
|
22
|
+
## Simple Properties
|
23
23
|
|
24
|
-
### `Persist.
|
24
|
+
### `Persist.Property.String`
|
25
25
|
|
26
26
|
Use the `String` type for model properties that should store a [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). The `String` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
27
27
|
|
28
28
|
```javascript
|
29
29
|
import Persist from '@acodeninja/persist';
|
30
30
|
|
31
|
-
class Person extends Persist.
|
31
|
+
class Person extends Persist.Model {
|
32
32
|
static {
|
33
|
-
|
34
|
-
|
33
|
+
Person.firstName = Persist.Property.String;
|
34
|
+
Person.lastName = Persist.Property.String.required;
|
35
35
|
}
|
36
36
|
}
|
37
37
|
```
|
38
38
|
|
39
|
-
### `Persist.
|
39
|
+
### `Persist.Property.Boolean`
|
40
40
|
|
41
41
|
Use the `Boolean` type for model properties that should store a [boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean). The `Boolean` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
42
42
|
|
43
43
|
```javascript
|
44
44
|
import Persist from '@acodeninja/persist';
|
45
45
|
|
46
|
-
class Person extends Persist.
|
46
|
+
class Person extends Persist.Model {
|
47
47
|
static {
|
48
|
-
|
49
|
-
|
48
|
+
Person.marketingEmailsActive = Persist.Property.Boolean;
|
49
|
+
Person.accountActive = Persist.Property.Boolean.required;
|
50
50
|
}
|
51
51
|
}
|
52
52
|
```
|
53
53
|
|
54
|
-
### `Persist.
|
54
|
+
### `Persist.Property.Number`
|
55
55
|
|
56
56
|
Use the `Number` type for model properties that should store a [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). The `Number` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
57
57
|
|
58
58
|
```javascript
|
59
59
|
import Persist from '@acodeninja/persist';
|
60
60
|
|
61
|
-
class Person extends Persist.
|
61
|
+
class Person extends Persist.Model {
|
62
62
|
static {
|
63
|
-
|
64
|
-
|
63
|
+
Person.loginToken = Persist.Property.Number;
|
64
|
+
Person.accountId = Persist.Property.Number.required;
|
65
65
|
}
|
66
66
|
}
|
67
67
|
```
|
68
68
|
|
69
|
-
### `Persist.
|
69
|
+
### `Persist.Property.Date`
|
70
70
|
|
71
71
|
Use the `Date` type for model properties that should store a [date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date). The `Date` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
72
72
|
|
73
|
+
A property of `Date` will serialise to an ISO 8601 date format like `2011-10-05T14:48:00.000Z` when `.toData` is called on a model.
|
74
|
+
|
73
75
|
```javascript
|
74
76
|
import Persist from '@acodeninja/persist';
|
75
77
|
|
76
|
-
class Person extends Persist.
|
78
|
+
class Person extends Persist.Model {
|
77
79
|
static {
|
78
|
-
|
79
|
-
|
80
|
+
Person.lastLogin = Persist.Property.Date;
|
81
|
+
Person.createdAt = Persist.Property.Date.required;
|
80
82
|
}
|
81
83
|
}
|
82
84
|
```
|
83
85
|
|
84
|
-
## Complex
|
86
|
+
## Complex Properties
|
85
87
|
|
86
|
-
### `Persist.
|
88
|
+
### `Persist.Property.Array.of(type)`
|
87
89
|
|
88
90
|
Use the `Array` type for model properties that should store an array of another type or model. The `Array` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
89
91
|
|
90
92
|
```javascript
|
91
93
|
import Persist from '@acodeninja/persist';
|
92
94
|
|
93
|
-
class Person extends Persist.
|
95
|
+
class Person extends Persist.Model {
|
94
96
|
static {
|
95
|
-
|
96
|
-
|
97
|
+
Person.failedLoginAttempts = Persist.Property.Array.of(Persist.Property.Date);
|
98
|
+
Person.fullName = Persist.Property.Array.of(Persist.Property.String).required;
|
97
99
|
}
|
98
100
|
}
|
99
101
|
```
|
100
102
|
|
101
|
-
### `Persist.
|
103
|
+
### `Persist.Property.Custom.of(schema)`
|
102
104
|
|
103
105
|
Use the `Custom` type for model properties that should store a custom [json-schema draft-07](https://json-schema.org/draft-07/json-schema-hypermedia) object. You can also use any formats defined by the [`avj-formats`](https://ajv.js.org/packages/ajv-formats.html) library. The `Custom` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
104
106
|
|
105
107
|
```javascript
|
106
108
|
import Persist from '@acodeninja/persist';
|
107
109
|
|
108
|
-
class Person extends Persist.
|
110
|
+
class Person extends Persist.Model {
|
109
111
|
static {
|
110
|
-
|
112
|
+
Person.address = Persist.Property.Custom.of({
|
111
113
|
type: 'object',
|
112
114
|
additionalProperties: false,
|
113
115
|
required: ['line1', 'city', 'postcode'],
|
@@ -125,21 +127,21 @@ class Person extends Persist.Type.Model {
|
|
125
127
|
}
|
126
128
|
```
|
127
129
|
|
128
|
-
## Resolved
|
130
|
+
## Resolved Properties
|
129
131
|
|
130
132
|
Resolved types are different from other types in that they do not directly store data themselves, rather they perform an action on another property of the model.
|
131
133
|
|
132
|
-
### `Persist.
|
134
|
+
### `Persist.Property.Resolved.Slug.of(property)`
|
133
135
|
|
134
136
|
Use the `Slug` type for model properties that should have a slug version of another properties value. The `Custom` type also supports the `.required` modifier to ensure that when the model is persisted a value must exist for it.
|
135
137
|
|
136
138
|
```javascript
|
137
139
|
import Persist from '@acodeninja/persist';
|
138
140
|
|
139
|
-
class Page extends Persist.
|
141
|
+
class Page extends Persist.Model {
|
140
142
|
static {
|
141
|
-
|
142
|
-
|
143
|
+
Page.title = Persist.Property.String;
|
144
|
+
Page.slug = Persist.Property.Resolved.Slug.of('title');
|
143
145
|
}
|
144
146
|
}
|
145
147
|
|
@@ -158,16 +160,47 @@ Models and most types support a modifier, this will alter the validation and per
|
|
158
160
|
Most types support the `.required` modifier, which will alter validation to enforce the presence of the property when saving data.
|
159
161
|
|
160
162
|
```javascript
|
161
|
-
class RequiredStringModel extends Persist.
|
163
|
+
class RequiredStringModel extends Persist.Model {
|
162
164
|
static {
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
165
|
+
RequiredStringModel.requiredString = Persist.Property.String.required;
|
166
|
+
RequiredStringModel.requiredNumber = Persist.Property.Number.required;
|
167
|
+
RequiredStringModel.requiredBoolean = Persist.Property.Boolean.required;
|
168
|
+
RequiredStringModel.requiredDate = Persist.Property.Date.required;
|
169
|
+
RequiredStringModel.requiredArrayOfString = Persist.Property.Array.of(Persist.Property.String).required;
|
170
|
+
RequiredStringModel.requiredArrayOfNumber = Persist.Property.Array.of(Persist.Property.Number).required;
|
171
|
+
RequiredStringModel.requiredArrayOfBoolean = Persist.Property.Array.of(Persist.Property.Boolean).required;
|
172
|
+
RequiredStringModel.requiredArrayOfDate = Persist.Property.Array.of(Persist.Property.Date).required;
|
171
173
|
}
|
172
174
|
}
|
173
175
|
```
|
176
|
+
|
177
|
+
## Custom Property Types
|
178
|
+
|
179
|
+
Under the hood, model validation uses the [ajv](https://ajv.js.org/) library with [ajv-formats](https://ajv.js.org/packages/ajv-formats.html) included. Because of this, you can create your own property types.
|
180
|
+
|
181
|
+
Say you want to attach an IPv4 address to your models. The following type class can accomplish this.
|
182
|
+
|
183
|
+
```javascript
|
184
|
+
import Persist from '@acodeninja/persist';
|
185
|
+
|
186
|
+
class IPv4Type extends Persist.Property.Type {
|
187
|
+
static {
|
188
|
+
// Set the type of the property to string
|
189
|
+
IPv4Type._type = 'string';
|
190
|
+
// Use the ajv extended format "ipv4"
|
191
|
+
IPv4Type._format = 'ipv4';
|
192
|
+
// Ensure that even when minified, the name of the constructor is IPv4
|
193
|
+
Object.defineProperty(IPv4Type, 'name', {value: 'IPv4'});
|
194
|
+
}
|
195
|
+
}
|
196
|
+
```
|
197
|
+
|
198
|
+
This type can then be used in any model as needed.
|
199
|
+
|
200
|
+
```javascript
|
201
|
+
import Persist from '@acodeninja/persist';
|
202
|
+
|
203
|
+
class StaticIP extends Persist.Model {
|
204
|
+
static ip = IPv4Type.required;
|
205
|
+
}
|
206
|
+
```
|