@decaf-ts/db-decorators 0.6.1 → 0.6.2
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/LICENSE.md +21 -157
- package/README.md +571 -10
- package/dist/db-decorators.cjs +1352 -322
- package/dist/db-decorators.esm.cjs +1352 -323
- package/lib/esm/identity/decorators.d.ts +7 -0
- package/lib/esm/identity/decorators.js +11 -4
- package/lib/esm/identity/index.js +3 -3
- package/lib/esm/identity/utils.d.ts +36 -23
- package/lib/esm/identity/utils.js +38 -25
- package/lib/esm/index.d.ts +12 -27
- package/lib/esm/index.js +13 -28
- package/lib/esm/interfaces/BulkCrudOperator.d.ts +39 -0
- package/lib/esm/interfaces/BulkCrudOperator.js +1 -1
- package/lib/esm/interfaces/Contextual.d.ts +17 -0
- package/lib/esm/interfaces/Contextual.js +1 -1
- package/lib/esm/interfaces/CrudOperator.d.ts +26 -23
- package/lib/esm/interfaces/CrudOperator.js +1 -1
- package/lib/esm/interfaces/IRepository.d.ts +10 -2
- package/lib/esm/interfaces/IRepository.js +1 -1
- package/lib/esm/interfaces/index.js +5 -5
- package/lib/esm/model/constants.d.ts +11 -13
- package/lib/esm/model/constants.js +12 -14
- package/lib/esm/model/decorators.d.ts +112 -23
- package/lib/esm/model/decorators.js +119 -29
- package/lib/esm/model/index.d.ts +1 -0
- package/lib/esm/model/index.js +7 -6
- package/lib/esm/model/model.d.ts +2 -141
- package/lib/esm/model/model.js +2 -13
- package/lib/esm/model/overrides.d.ts +1 -0
- package/lib/esm/model/overrides.js +23 -0
- package/lib/esm/model/utils.d.ts +39 -0
- package/lib/esm/model/utils.js +42 -3
- package/lib/esm/model/validation.d.ts +26 -8
- package/lib/esm/model/validation.js +29 -11
- package/lib/esm/operations/Operations.d.ts +65 -3
- package/lib/esm/operations/Operations.js +68 -6
- package/lib/esm/operations/OperationsRegistry.d.ts +44 -16
- package/lib/esm/operations/OperationsRegistry.js +46 -18
- package/lib/esm/operations/constants.d.ts +27 -8
- package/lib/esm/operations/constants.js +16 -9
- package/lib/esm/operations/decorators.d.ts +140 -134
- package/lib/esm/operations/decorators.js +152 -137
- package/lib/esm/operations/index.js +6 -6
- package/lib/esm/operations/types.d.ts +10 -0
- package/lib/esm/operations/types.js +1 -1
- package/lib/esm/repository/BaseRepository.d.ts +322 -0
- package/lib/esm/repository/BaseRepository.js +297 -7
- package/lib/esm/repository/Context.d.ts +153 -2
- package/lib/esm/repository/Context.js +154 -6
- package/lib/esm/repository/Repository.d.ts +89 -0
- package/lib/esm/repository/Repository.js +96 -7
- package/lib/esm/repository/constants.d.ts +7 -0
- package/lib/esm/repository/constants.js +8 -1
- package/lib/esm/repository/errors.d.ts +61 -34
- package/lib/esm/repository/errors.js +62 -35
- package/lib/esm/repository/index.js +9 -9
- package/lib/esm/repository/types.d.ts +25 -0
- package/lib/esm/repository/types.js +1 -1
- package/lib/esm/repository/utils.d.ts +11 -0
- package/lib/esm/repository/utils.js +4 -4
- package/lib/esm/repository/wrappers.d.ts +2 -2
- package/lib/esm/repository/wrappers.js +5 -5
- package/lib/esm/validation/constants.d.ts +20 -5
- package/lib/esm/validation/constants.js +22 -7
- package/lib/esm/validation/decorators.d.ts +101 -19
- package/lib/esm/validation/decorators.js +109 -27
- package/lib/esm/validation/index.js +5 -5
- package/lib/esm/validation/validation.js +10 -2
- package/lib/esm/validation/validators/ReadOnlyValidator.d.ts +32 -8
- package/lib/esm/validation/validators/ReadOnlyValidator.js +34 -10
- package/lib/esm/validation/validators/TimestampValidator.d.ts +37 -3
- package/lib/esm/validation/validators/TimestampValidator.js +39 -5
- package/lib/esm/validation/validators/UpdateValidator.d.ts +28 -11
- package/lib/esm/validation/validators/UpdateValidator.js +23 -8
- package/lib/esm/validation/validators/index.js +4 -4
- package/lib/identity/decorators.cjs +8 -1
- package/lib/identity/decorators.d.ts +7 -0
- package/lib/identity/utils.cjs +35 -22
- package/lib/identity/utils.d.ts +36 -23
- package/lib/index.cjs +14 -28
- package/lib/index.d.ts +12 -27
- package/lib/interfaces/BulkCrudOperator.cjs +1 -1
- package/lib/interfaces/BulkCrudOperator.d.ts +39 -0
- package/lib/interfaces/Contextual.cjs +1 -1
- package/lib/interfaces/Contextual.d.ts +17 -0
- package/lib/interfaces/CrudOperator.cjs +1 -1
- package/lib/interfaces/CrudOperator.d.ts +26 -23
- package/lib/interfaces/IRepository.cjs +1 -1
- package/lib/interfaces/IRepository.d.ts +10 -2
- package/lib/model/constants.cjs +12 -14
- package/lib/model/constants.d.ts +11 -13
- package/lib/model/decorators.cjs +114 -24
- package/lib/model/decorators.d.ts +112 -23
- package/lib/model/index.cjs +2 -1
- package/lib/model/index.d.ts +1 -0
- package/lib/model/model.cjs +1 -13
- package/lib/model/model.d.ts +2 -141
- package/lib/model/overrides.cjs +25 -0
- package/lib/model/overrides.d.ts +1 -0
- package/lib/model/utils.cjs +40 -1
- package/lib/model/utils.d.ts +39 -0
- package/lib/model/validation.cjs +27 -9
- package/lib/model/validation.d.ts +26 -8
- package/lib/operations/Operations.cjs +66 -4
- package/lib/operations/Operations.d.ts +65 -3
- package/lib/operations/OperationsRegistry.cjs +45 -17
- package/lib/operations/OperationsRegistry.d.ts +44 -16
- package/lib/operations/constants.cjs +16 -9
- package/lib/operations/constants.d.ts +27 -8
- package/lib/operations/decorators.cjs +150 -135
- package/lib/operations/decorators.d.ts +140 -134
- package/lib/operations/types.cjs +1 -1
- package/lib/operations/types.d.ts +10 -0
- package/lib/repository/BaseRepository.cjs +291 -1
- package/lib/repository/BaseRepository.d.ts +322 -0
- package/lib/repository/Context.cjs +153 -5
- package/lib/repository/Context.d.ts +153 -2
- package/lib/repository/Repository.cjs +90 -1
- package/lib/repository/Repository.d.ts +89 -0
- package/lib/repository/constants.cjs +8 -1
- package/lib/repository/constants.d.ts +7 -0
- package/lib/repository/errors.cjs +62 -35
- package/lib/repository/errors.d.ts +61 -34
- package/lib/repository/types.cjs +1 -1
- package/lib/repository/types.d.ts +25 -0
- package/lib/repository/utils.cjs +1 -1
- package/lib/repository/utils.d.ts +11 -0
- package/lib/repository/wrappers.cjs +3 -3
- package/lib/repository/wrappers.d.ts +2 -2
- package/lib/validation/constants.cjs +21 -6
- package/lib/validation/constants.d.ts +20 -5
- package/lib/validation/decorators.cjs +102 -20
- package/lib/validation/decorators.d.ts +101 -19
- package/lib/validation/validation.cjs +9 -1
- package/lib/validation/validators/ReadOnlyValidator.cjs +33 -9
- package/lib/validation/validators/ReadOnlyValidator.d.ts +32 -8
- package/lib/validation/validators/TimestampValidator.cjs +38 -4
- package/lib/validation/validators/TimestampValidator.d.ts +37 -3
- package/lib/validation/validators/UpdateValidator.cjs +23 -8
- package/lib/validation/validators/UpdateValidator.d.ts +28 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|

|
|
2
|
-
|
|
2
|
+
### DB-Decorators: Database Operations Made Simple
|
|
3
|
+
|
|
4
|
+
The db-decorators library provides a comprehensive set of TypeScript decorators and utilities for database operations. It implements the repository pattern with support for model definition, validation, identity management, and operation hooks. The library enables developers to define database models with decorators, perform CRUD operations with validation, and customize behavior during database operations through operation hooks.
|
|
3
5
|
|
|
4
|
-
Extension to Decorator Validation with common db functionalities
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|

|
|
@@ -27,15 +28,41 @@ Extension to Decorator Validation with common db functionalities
|
|
|
27
28
|
|
|
28
29
|
Documentation available [here](https://decaf-ts.github.io/db-decorators/)
|
|
29
30
|
|
|
30
|
-
### Description
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
### DB-Decorators
|
|
33
|
+
|
|
34
|
+
The db-decorators library is a powerful TypeScript framework for database operations that leverages decorators to simplify database interactions. It provides a comprehensive solution for implementing the repository pattern with built-in support for model definition, validation, identity management, and operation hooks.
|
|
35
|
+
|
|
36
|
+
#### Key Features
|
|
37
|
+
|
|
38
|
+
1. **Repository Pattern Implementation**
|
|
39
|
+
- Abstract `BaseRepository` class providing the foundation for CRUD operations
|
|
40
|
+
- Concrete `Repository` class with validation support
|
|
41
|
+
- Support for bulk operations through the `BulkCrudOperator` interface
|
|
42
|
+
|
|
43
|
+
2. **Model Management**
|
|
44
|
+
- Model definition with TypeScript decorators
|
|
45
|
+
- Identity management with the `@id()` decorator
|
|
46
|
+
- Property composition with `@composed()` and `@composedFromKeys()` decorators
|
|
47
|
+
- Versioning support with the `@version()` decorator
|
|
48
|
+
- Transient properties with the `@transient()` decorator
|
|
49
|
+
|
|
50
|
+
3. **Operation Hooks**
|
|
51
|
+
- Pre-operation hooks with `@on()`, `@onCreate()`, `@onUpdate()`, etc.
|
|
52
|
+
- Post-operation hooks with `@after()`, `@afterCreate()`, `@afterUpdate()`, etc.
|
|
53
|
+
- Custom operation handlers through the `Operations` registry
|
|
54
|
+
|
|
55
|
+
4. **Context Management**
|
|
56
|
+
- Hierarchical context chains with parent-child relationships
|
|
57
|
+
- Context accumulation for state management
|
|
58
|
+
- Operation-specific context creation
|
|
59
|
+
|
|
60
|
+
5. **Validation**
|
|
61
|
+
- Integration with decorator-validation library
|
|
62
|
+
- Automatic validation during CRUD operations
|
|
63
|
+
- Custom validation rules through decorators
|
|
64
|
+
|
|
65
|
+
The library is designed to be extensible and adaptable to different database backends, providing a consistent API regardless of the underlying storage mechanism.
|
|
39
66
|
|
|
40
67
|
|
|
41
68
|
### How to Use
|
|
@@ -43,6 +70,540 @@ Extension of `decorator-validation`, extends de validation api for the Model cla
|
|
|
43
70
|
- [Initial Setup](../../workdocs/tutorials/For%20Developers.md#_initial-setup_)
|
|
44
71
|
- [Installation](../../workdocs/tutorials/For%20Developers.md#installation)
|
|
45
72
|
|
|
73
|
+
## DB-Decorators Examples
|
|
74
|
+
|
|
75
|
+
### 1. Defining Models with Decorators
|
|
76
|
+
|
|
77
|
+
#### Basic Model Definition
|
|
78
|
+
|
|
79
|
+
Description: Create a User model with ID, name, email, and password fields. The ID is marked as required and readonly, the password is hashed, and the email is required.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
83
|
+
import { id, hash, version, transient } from "@decaf-ts/db-decorators";
|
|
84
|
+
import { required, minLength, email as emailValidator } from "@decaf-ts/decorator-validation";
|
|
85
|
+
|
|
86
|
+
class User extends Model {
|
|
87
|
+
@id()
|
|
88
|
+
id: string;
|
|
89
|
+
|
|
90
|
+
@required()
|
|
91
|
+
@minLength(3)
|
|
92
|
+
name: string;
|
|
93
|
+
|
|
94
|
+
@required()
|
|
95
|
+
@emailValidator()
|
|
96
|
+
email: string;
|
|
97
|
+
|
|
98
|
+
@required()
|
|
99
|
+
@minLength(8)
|
|
100
|
+
@hash()
|
|
101
|
+
password: string;
|
|
102
|
+
|
|
103
|
+
@version()
|
|
104
|
+
version: number;
|
|
105
|
+
|
|
106
|
+
@transient()
|
|
107
|
+
temporaryData: any;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Composed Properties
|
|
112
|
+
|
|
113
|
+
Description: Create a Product model with a SKU that is automatically composed from other properties.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
117
|
+
import { id, composed, composedFromKeys } from "@decaf-ts/db-decorators";
|
|
118
|
+
import { required } from "@decaf-ts/decorator-validation";
|
|
119
|
+
|
|
120
|
+
class Product extends Model {
|
|
121
|
+
@id()
|
|
122
|
+
id: string;
|
|
123
|
+
|
|
124
|
+
@required()
|
|
125
|
+
category: string;
|
|
126
|
+
|
|
127
|
+
@required()
|
|
128
|
+
name: string;
|
|
129
|
+
|
|
130
|
+
@required()
|
|
131
|
+
variant: string;
|
|
132
|
+
|
|
133
|
+
@composed(['category', 'name', 'variant'], '-')
|
|
134
|
+
sku: string;
|
|
135
|
+
|
|
136
|
+
@composedFromKeys(['category', 'name'], '_', true, 'PROD_', '_KEY')
|
|
137
|
+
productKey: string;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. Implementing Repositories
|
|
142
|
+
|
|
143
|
+
#### Basic Repository Implementation
|
|
144
|
+
|
|
145
|
+
Description: Create a repository for the User model that implements the required CRUD operations.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { Repository } from "@decaf-ts/db-decorators";
|
|
149
|
+
import { User } from "./models/User";
|
|
150
|
+
|
|
151
|
+
class UserRepository extends Repository<User> {
|
|
152
|
+
constructor() {
|
|
153
|
+
super(User);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async create(model: User, ...args: any[]): Promise<User> {
|
|
157
|
+
// Implementation for creating a user in the database
|
|
158
|
+
console.log(`Creating user: ${model.name}`);
|
|
159
|
+
// Assign an ID if not already present
|
|
160
|
+
if (!model.id) {
|
|
161
|
+
model.id = Date.now().toString();
|
|
162
|
+
}
|
|
163
|
+
return model;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async read(key: string | number, ...args: any[]): Promise<User> {
|
|
167
|
+
// Implementation for reading a user from the database
|
|
168
|
+
console.log(`Reading user with ID: ${key}`);
|
|
169
|
+
return new User({ id: key, name: "Example User", email: "user@example.com" });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async update(model: User, ...args: any[]): Promise<User> {
|
|
173
|
+
// Implementation for updating a user in the database
|
|
174
|
+
console.log(`Updating user: ${model.name}`);
|
|
175
|
+
return model;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async delete(key: string | number, ...args: any[]): Promise<User> {
|
|
179
|
+
// Implementation for deleting a user from the database
|
|
180
|
+
console.log(`Deleting user with ID: ${key}`);
|
|
181
|
+
const user = await this.read(key);
|
|
182
|
+
return user;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Using Bulk Operations
|
|
188
|
+
|
|
189
|
+
Description: Implement bulk operations for efficient batch processing of multiple models.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { Repository } from "@decaf-ts/db-decorators";
|
|
193
|
+
import { Product } from "./models/Product";
|
|
194
|
+
|
|
195
|
+
class ProductRepository extends Repository<Product> {
|
|
196
|
+
constructor() {
|
|
197
|
+
super(Product);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Implement required CRUD methods
|
|
201
|
+
async create(model: Product, ...args: any[]): Promise<Product> {
|
|
202
|
+
// Implementation
|
|
203
|
+
return model;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async read(key: string | number, ...args: any[]): Promise<Product> {
|
|
207
|
+
// Implementation
|
|
208
|
+
return new Product({ id: key });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async update(model: Product, ...args: any[]): Promise<Product> {
|
|
212
|
+
// Implementation
|
|
213
|
+
return model;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async delete(key: string | number, ...args: any[]): Promise<Product> {
|
|
217
|
+
// Implementation
|
|
218
|
+
return await this.read(key);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Override bulk methods for optimized implementation
|
|
222
|
+
async createAll(models: Product[], ...args: any[]): Promise<Product[]> {
|
|
223
|
+
console.log(`Bulk creating ${models.length} products`);
|
|
224
|
+
// Custom implementation for bulk creation
|
|
225
|
+
return models.map(model => {
|
|
226
|
+
if (!model.id) {
|
|
227
|
+
model.id = Date.now().toString();
|
|
228
|
+
}
|
|
229
|
+
return model;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async readAll(keys: string[] | number[], ...args: any[]): Promise<Product[]> {
|
|
234
|
+
console.log(`Bulk reading ${keys.length} products`);
|
|
235
|
+
// Custom implementation for bulk reading
|
|
236
|
+
return keys.map(key => new Product({ id: key }));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 3. Using Operation Hooks
|
|
242
|
+
|
|
243
|
+
#### Property Transformation Hooks
|
|
244
|
+
|
|
245
|
+
Description: Use operation hooks to transform property values during database operations.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
249
|
+
import { id, onCreate, onUpdate, onCreateUpdate } from "@decaf-ts/db-decorators";
|
|
250
|
+
import { required } from "@decaf-ts/decorator-validation";
|
|
251
|
+
|
|
252
|
+
// Handler function for setting creation timestamp
|
|
253
|
+
function setCreationTimestamp(repo, context, data, key, model) {
|
|
254
|
+
model[key] = new Date().toISOString();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Handler function for setting update timestamp
|
|
258
|
+
function setUpdateTimestamp(repo, context, data, key, model) {
|
|
259
|
+
model[key] = new Date().toISOString();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Handler function for normalizing email
|
|
263
|
+
function normalizeEmail(repo, context, data, key, model) {
|
|
264
|
+
if (model[key]) {
|
|
265
|
+
model[key] = model[key].toLowerCase().trim();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
class User extends Model {
|
|
270
|
+
@id()
|
|
271
|
+
id: string;
|
|
272
|
+
|
|
273
|
+
@required()
|
|
274
|
+
name: string;
|
|
275
|
+
|
|
276
|
+
@required()
|
|
277
|
+
@onCreateUpdate(normalizeEmail)
|
|
278
|
+
email: string;
|
|
279
|
+
|
|
280
|
+
@onCreate(setCreationTimestamp)
|
|
281
|
+
createdAt: string;
|
|
282
|
+
|
|
283
|
+
@onUpdate(setUpdateTimestamp)
|
|
284
|
+
updatedAt: string;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### Post-Operation Hooks
|
|
289
|
+
|
|
290
|
+
Description: Use post-operation hooks to perform actions after database operations.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
294
|
+
import { id, afterCreate, afterUpdate, afterDelete } from "@decaf-ts/db-decorators";
|
|
295
|
+
|
|
296
|
+
// Handler function for logging after creation
|
|
297
|
+
function logCreation(repo, context, data, key, model) {
|
|
298
|
+
console.log(`User created: ${model.id} - ${model.name}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Handler function for logging after update
|
|
302
|
+
function logUpdate(repo, context, data, key, model) {
|
|
303
|
+
console.log(`User updated: ${model.id} - ${model.name}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Handler function for logging after deletion
|
|
307
|
+
function logDeletion(repo, context, data, key, model) {
|
|
308
|
+
console.log(`User deleted: ${model.id} - ${model.name}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
class User extends Model {
|
|
312
|
+
@id()
|
|
313
|
+
id: string;
|
|
314
|
+
|
|
315
|
+
@required()
|
|
316
|
+
name: string;
|
|
317
|
+
|
|
318
|
+
@required()
|
|
319
|
+
email: string;
|
|
320
|
+
|
|
321
|
+
@afterCreate(logCreation)
|
|
322
|
+
@afterUpdate(logUpdate)
|
|
323
|
+
@afterDelete(logDeletion)
|
|
324
|
+
_log: any; // This property is just a placeholder for the decorators
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 4. Working with Contexts
|
|
329
|
+
|
|
330
|
+
#### Creating and Using Contexts
|
|
331
|
+
|
|
332
|
+
Description: Create and use contexts to manage operation state and configuration.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { Context, Repository } from "@decaf-ts/db-decorators";
|
|
336
|
+
import { User } from "./models/User";
|
|
337
|
+
|
|
338
|
+
class UserRepository extends Repository<User> {
|
|
339
|
+
constructor() {
|
|
340
|
+
super(User);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Implement required CRUD methods
|
|
344
|
+
async create(model: User, ...args: any[]): Promise<User> {
|
|
345
|
+
// Implementation
|
|
346
|
+
return model;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async read(key: string | number, ...args: any[]): Promise<User> {
|
|
350
|
+
// Implementation
|
|
351
|
+
return new User({ id: key });
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async update(model: User, ...args: any[]): Promise<User> {
|
|
355
|
+
// Implementation
|
|
356
|
+
return model;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async delete(key: string | number, ...args: any[]): Promise<User> {
|
|
360
|
+
// Implementation
|
|
361
|
+
return await this.read(key);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Example of using context
|
|
365
|
+
async createWithAudit(model: User, userId: string): Promise<User> {
|
|
366
|
+
// Create a context with audit information
|
|
367
|
+
const context = new Context().accumulate({
|
|
368
|
+
auditUser: userId,
|
|
369
|
+
auditTimestamp: new Date(),
|
|
370
|
+
skipValidation: false
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Pass the context to the create method
|
|
374
|
+
return this.create(model, context);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Usage
|
|
379
|
+
const userRepo = new UserRepository();
|
|
380
|
+
const newUser = new User({ name: "John Doe", email: "john@example.com" });
|
|
381
|
+
const createdUser = await userRepo.createWithAudit(newUser, "admin123");
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Context Hierarchies
|
|
385
|
+
|
|
386
|
+
Description: Create hierarchical contexts for complex operations.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { Context, OperationKeys } from "@decaf-ts/db-decorators";
|
|
390
|
+
import { User } from "./models/User";
|
|
391
|
+
|
|
392
|
+
// Create a parent context
|
|
393
|
+
const parentContext = new Context().accumulate({
|
|
394
|
+
transactionId: "tx123",
|
|
395
|
+
batchOperation: true
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Create a child context for a specific operation
|
|
399
|
+
const childContext = parentContext.child<User, Context<any>>(
|
|
400
|
+
OperationKeys.CREATE,
|
|
401
|
+
User
|
|
402
|
+
).accumulate({
|
|
403
|
+
operationId: "op456",
|
|
404
|
+
validationLevel: "strict"
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Access values from the context hierarchy
|
|
408
|
+
console.log(childContext.get("transactionId")); // "tx123" (inherited from parent)
|
|
409
|
+
console.log(childContext.get("operationId")); // "op456" (from child)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 5. Performing CRUD Operations
|
|
413
|
+
|
|
414
|
+
#### Basic CRUD Operations
|
|
415
|
+
|
|
416
|
+
Description: Perform basic CRUD operations using a repository.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import { User } from "./models/User";
|
|
420
|
+
import { UserRepository } from "./repositories/UserRepository";
|
|
421
|
+
|
|
422
|
+
async function userCrudExample() {
|
|
423
|
+
const userRepo = new UserRepository();
|
|
424
|
+
|
|
425
|
+
// Create a new user
|
|
426
|
+
const newUser = new User({
|
|
427
|
+
name: "Alice Smith",
|
|
428
|
+
email: "alice@example.com",
|
|
429
|
+
password: "securePassword123"
|
|
430
|
+
});
|
|
431
|
+
const createdUser = await userRepo.create(newUser);
|
|
432
|
+
console.log("Created user:", createdUser);
|
|
433
|
+
|
|
434
|
+
// Read a user
|
|
435
|
+
const userId = createdUser.id;
|
|
436
|
+
const retrievedUser = await userRepo.read(userId);
|
|
437
|
+
console.log("Retrieved user:", retrievedUser);
|
|
438
|
+
|
|
439
|
+
// Update a user
|
|
440
|
+
retrievedUser.name = "Alice Johnson";
|
|
441
|
+
const updatedUser = await userRepo.update(retrievedUser);
|
|
442
|
+
console.log("Updated user:", updatedUser);
|
|
443
|
+
|
|
444
|
+
// Delete a user
|
|
445
|
+
const deletedUser = await userRepo.delete(userId);
|
|
446
|
+
console.log("Deleted user:", deletedUser);
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### Bulk Operations
|
|
451
|
+
|
|
452
|
+
Description: Perform bulk operations for efficient batch processing.
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
import { Product } from "./models/Product";
|
|
456
|
+
import { ProductRepository } from "./repositories/ProductRepository";
|
|
457
|
+
|
|
458
|
+
async function productBulkExample() {
|
|
459
|
+
const productRepo = new ProductRepository();
|
|
460
|
+
|
|
461
|
+
// Create multiple products
|
|
462
|
+
const products = [
|
|
463
|
+
new Product({ category: "Electronics", name: "Laptop", variant: "15-inch" }),
|
|
464
|
+
new Product({ category: "Electronics", name: "Laptop", variant: "13-inch" }),
|
|
465
|
+
new Product({ category: "Electronics", name: "Smartphone", variant: "Pro" })
|
|
466
|
+
];
|
|
467
|
+
const createdProducts = await productRepo.createAll(products);
|
|
468
|
+
console.log("Created products:", createdProducts);
|
|
469
|
+
|
|
470
|
+
// Read multiple products
|
|
471
|
+
const productIds = createdProducts.map(p => p.id);
|
|
472
|
+
const retrievedProducts = await productRepo.readAll(productIds);
|
|
473
|
+
console.log("Retrieved products:", retrievedProducts);
|
|
474
|
+
|
|
475
|
+
// Update multiple products
|
|
476
|
+
const updatedProducts = retrievedProducts.map(p => {
|
|
477
|
+
p.name = p.name + " (Updated)";
|
|
478
|
+
return p;
|
|
479
|
+
});
|
|
480
|
+
const savedProducts = await productRepo.updateAll(updatedProducts);
|
|
481
|
+
console.log("Updated products:", savedProducts);
|
|
482
|
+
|
|
483
|
+
// Delete multiple products
|
|
484
|
+
const deletedProducts = await productRepo.deleteAll(productIds);
|
|
485
|
+
console.log("Deleted products:", deletedProducts);
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 6. Validation
|
|
490
|
+
|
|
491
|
+
#### Model Validation
|
|
492
|
+
|
|
493
|
+
Description: Validate models during CRUD operations to ensure data integrity.
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
import { Model, ModelErrorDefinition } from "@decaf-ts/decorator-validation";
|
|
497
|
+
import { id } from "@decaf-ts/db-decorators";
|
|
498
|
+
import { required, minLength, maxLength, email, pattern } from "@decaf-ts/decorator-validation";
|
|
499
|
+
|
|
500
|
+
class User extends Model {
|
|
501
|
+
@id()
|
|
502
|
+
id: string;
|
|
503
|
+
|
|
504
|
+
@required()
|
|
505
|
+
@minLength(2)
|
|
506
|
+
@maxLength(50)
|
|
507
|
+
name: string;
|
|
508
|
+
|
|
509
|
+
@required()
|
|
510
|
+
@email()
|
|
511
|
+
emailAddress: string;
|
|
512
|
+
|
|
513
|
+
@required()
|
|
514
|
+
@minLength(8)
|
|
515
|
+
@pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
|
|
516
|
+
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character")
|
|
517
|
+
password: string;
|
|
518
|
+
|
|
519
|
+
// Manual validation example
|
|
520
|
+
hasErrors(): ModelErrorDefinition | undefined {
|
|
521
|
+
const errors = super.hasErrors();
|
|
522
|
+
|
|
523
|
+
// Add custom validation logic
|
|
524
|
+
if (this.name && this.name.includes('admin') && !this.emailAddress.includes('admin')) {
|
|
525
|
+
if (!errors) {
|
|
526
|
+
return {
|
|
527
|
+
name: ["Admin users must have an admin email address"]
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
errors.name = errors.name || [];
|
|
531
|
+
errors.name.push("Admin users must have an admin email address");
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return errors;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Usage in a repository
|
|
539
|
+
class UserRepository extends Repository<User> {
|
|
540
|
+
// ... other methods
|
|
541
|
+
|
|
542
|
+
async create(model: User, ...args: any[]): Promise<User> {
|
|
543
|
+
// The Repository class will automatically validate the model
|
|
544
|
+
// and throw a ValidationError if validation fails
|
|
545
|
+
|
|
546
|
+
// Custom validation can also be performed
|
|
547
|
+
const errors = model.hasErrors();
|
|
548
|
+
if (errors) {
|
|
549
|
+
throw new ValidationError(errors.toString());
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Proceed with creation if validation passes
|
|
553
|
+
return model;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 7. Identity Management
|
|
559
|
+
|
|
560
|
+
#### Working with Model IDs
|
|
561
|
+
|
|
562
|
+
Description: Use the identity module to work with model IDs.
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import { Model } from "@decaf-ts/decorator-validation";
|
|
566
|
+
import { id } from "@decaf-ts/db-decorators";
|
|
567
|
+
import { findPrimaryKey, findModelId } from "@decaf-ts/db-decorators";
|
|
568
|
+
|
|
569
|
+
class Document extends Model {
|
|
570
|
+
@id()
|
|
571
|
+
documentId: string;
|
|
572
|
+
|
|
573
|
+
title: string;
|
|
574
|
+
content: string;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Create a document instance
|
|
578
|
+
const doc = new Document({
|
|
579
|
+
documentId: "doc-123",
|
|
580
|
+
title: "Sample Document",
|
|
581
|
+
content: "This is a sample document."
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// Find the primary key property
|
|
585
|
+
const pkInfo = findPrimaryKey(doc);
|
|
586
|
+
console.log("Primary key property:", pkInfo.id); // "documentId"
|
|
587
|
+
console.log("Primary key metadata:", pkInfo.props);
|
|
588
|
+
|
|
589
|
+
// Get the primary key value
|
|
590
|
+
const docId = findModelId(doc);
|
|
591
|
+
console.log("Document ID:", docId); // "doc-123"
|
|
592
|
+
|
|
593
|
+
// Try to get ID from a model without an ID value
|
|
594
|
+
const emptyDoc = new Document();
|
|
595
|
+
try {
|
|
596
|
+
const id = findModelId(emptyDoc); // Will throw an error
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.error("Error:", error.message);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Get ID with returnEmpty option
|
|
602
|
+
const emptyId = findModelId(emptyDoc, true); // Returns undefined instead of throwing
|
|
603
|
+
console.log("Empty ID:", emptyId);
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
|
|
46
607
|
|
|
47
608
|
|
|
48
609
|
|