@keltoi/hydra 2.0.0 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +353 -1
  2. package/index.js +10 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,2 +1,354 @@
1
1
  # HydraJs
2
- Boilerplate to start your API from zero using Repository Pattern
2
+
3
+ HydraJs is a library that provides a set of classes to implement the Repository Pattern in your code. By using these classes, you can abstract away the data storage and retrieval logic, making it easier to switch between different data storage systems. It uses `Knex.js`, a popular SQL query builder for Node.js that allows you to build and execute SQL queries in a safe and efficient way. Knex provides a simple and intuitive API for building queries, and supports a wide range of database management systems, including MySQL, PostgreSQL, SQLite, and more.
4
+
5
+ For more information about Knex.js, including documentation, tutorials, and examples, visit the official Knex.js website.
6
+
7
+ https://knexjs.org/
8
+
9
+ For APIContext, HydraJs uses Axios, a popular JavaScript library used for making HTTP requests in Node.js and web applications. It provides a simple and intuitive way to send HTTP requests and interact with web servers.
10
+
11
+ https://axios-http.com/
12
+
13
+ **Key Features**
14
+ ----------------
15
+
16
+ * **Modular Design**: Hydra.JS is designed to be highly modular, allowing developers to easily add or remove features as needed.
17
+ * **Extensible Architecture**: The framework's architecture is designed to be extensible, making it easy to add new features and functionality.
18
+ * **Flexible Database Integration**: Hydra.JS provides a flexible way to integrate with various databases, making it easy to switch between different databases as needed.
19
+ * **Robust Query Execution**: The framework provides a robust way to execute queries, including support for transactions and batch operations.
20
+ * **Efficient Data Management**: Hydra.JS provides an efficient way to manage data, including support for caching and data validation.
21
+
22
+ ## Provided Abstractions
23
+ ---------
24
+
25
+ ### Entity
26
+ -----------
27
+
28
+ The `Entity` class represents a single entity in your data storage system. It is a fundamental class in the HydraJs library, and it plays a crucial role in representing a single entity in your data storage system.
29
+
30
+ **Class Definition**
31
+
32
+ The `Entity` class is defined as follows:
33
+ ```javascript
34
+ Class Entity:
35
+ Fields:
36
+ #key
37
+ #data
38
+ constructor(key = {}, data = {}) {
39
+ this.#data = data;
40
+ this.#key = key;
41
+ }
42
+ Methods:
43
+ key()
44
+ data()
45
+ $()
46
+ migrations()
47
+ structMe(db = knex())
48
+ build({ })
49
+ fromResult(result = new Result())
50
+ ```
51
+ **Fields**
52
+
53
+ The `Entity` class has two private fields:
54
+
55
+ * `#key`: This field represents the unique identifier of the entity. It is an object that contains the key-value pairs that uniquely identify the entity.
56
+ * `#data`: This field represents the data associated with the entity. It is an object that contains the key-value pairs that represent the entity's data.
57
+
58
+ **Constructor**
59
+
60
+ The `Entity` class has a constructor that takes two optional parameters:
61
+
62
+ * `key`: This parameter is an object that represents the unique identifier of the entity. If not provided, an empty object is used.
63
+ * `data`: This parameter is an object that represents the data associated with the entity. If not provided, an empty object is used.
64
+
65
+ The constructor initializes the `#key` and `#data` fields with the provided values.
66
+
67
+ **Methods**
68
+
69
+ The `Entity` class has several methods that provide various functionalities:
70
+
71
+ * `key()`: This method returns the `#key` field, which represents the unique identifier of the entity.
72
+ * `data()`: This method returns the `#data` field, which represents the data associated with the entity.
73
+ * `$()`: This method returns a new object that contains the merged `#key` and `#data` fields. This method is useful for creating a single object that represents the entity's data and key.
74
+ * `migrations()`: This method is not implemented in the provided code snippet, but it is likely used to handle database migrations for the entity.
75
+ * `structMe(db = knex())`: This method takes a `db` parameter, which is an instance of the `knex` library. The method returns a new object that represents the entity's structure.
76
+ * `build({ })`: This method takes an object with key-value pairs that represent the entity's data. The method returns a new `Entity` instance with the provided data.
77
+ * `fromResult(result = new Result())`: This method takes a `result` parameter, which is an instance of the `Result` class. The method returns a new `Entity` instance with the data from the `result` object.
78
+
79
+ **Usage**
80
+
81
+ The `Entity` class can be used to represent a single entity in your data storage system. For example:
82
+ ```javascript
83
+ const entity = new Entity({ id: 1 }, { name: 'John Doe' });
84
+ console.log(entity.key()); // Output: { id: 1 }
85
+ console.log(entity.data()); // Output: { name: 'John Doe' }
86
+ console.log(entity.$()); // Output: { id: 1, name: 'John Doe' }
87
+ ```
88
+ Note that this is just a basic example, and you will need to customize the usage to fit your specific use case.
89
+
90
+ ### Model
91
+ ------------
92
+
93
+ The `Model` class is an abstract class that represents a data model. It has a constructor that takes an object with properties that will be used to initialize the model and an abstract method `validate` that can be override to cover specifics validation scenario
94
+
95
+ ### Linking
96
+ ------------
97
+
98
+ The `Linking` class represents a relationship between two entities. It has properties for `abscissa` and `ordinate`, which represent the two entities being linked as a cartesian model.
99
+
100
+ ### Repository
101
+ -------------
102
+
103
+ The `Repository` class is an abstract class that provides a set of methods for creating, reading, updating, and deleting entities in a data storage system. It plays several roles in the HydraJS library:
104
+
105
+ * **Data Abstraction**: The `Repository` class abstracts the data storage and retrieval logic, making it easier to switch between different data storage systems.
106
+ * **Entity Management**: The `Repository` class is responsible for managing entities in a data storage system, including creating, reading, updating, and deleting entities.
107
+ * **Context Management**: The `Repository` class is responsible for managing the context in which it operates, including database connections, API endpoints, and other data storage systems.
108
+
109
+ **Class Definition**
110
+ --------------------
111
+
112
+ The `Repository` class is defined as follows:
113
+ ```javascript
114
+ Class Repository:
115
+ Fields:
116
+ #entity
117
+ #context
118
+ constructor(entity = Entity, context = Context) {
119
+ this.#entity = entity;
120
+ this.#context = context;
121
+ }
122
+ Methods:
123
+ create(entity)
124
+ insert(entity)
125
+ get(entity)
126
+ update(entity)
127
+ delete(entity)
128
+ list()
129
+ ```
130
+ **Fields**
131
+ ----------
132
+
133
+ The `Repository` class has two private fields:
134
+
135
+ * `#entity`: This field represents the entity class that the repository is responsible for managing.
136
+ * `#context`: This field represents the context in which the repository operates. The context can be a database, an API, or any other data storage system.
137
+
138
+ **Constructor**
139
+ --------------
140
+
141
+ The `Repository` class has a constructor that takes two optional parameters:
142
+
143
+ * `entity`: This parameter is the entity class that the repository is responsible for managing. If not provided, the `Entity` class is used by default.
144
+ * `context`: This parameter is the context in which the repository operates. If not provided, the `Context` class is used by default.
145
+
146
+ The constructor initializes the `#entity` and `#context` fields with the provided values.
147
+
148
+ **Methods**
149
+ ------------
150
+
151
+ The `Repository` class provides a set of methods for creating, reading, updating, and deleting entities in a data storage system:
152
+
153
+ * `include(entity)`: This method includes an entity in the data storage system.
154
+ * `create(entity)`: This method includes a new entity in the data storage system and returns the new `key` created in database. The difference between `include` and `create` is that the first one entry a new data in storage system, and is accountable for manages it's ids on database, so it can includes an autoincrement key and retrieve the created id.
155
+ * `get(entity)`: This method retrieves an entity from the data storage system based on its unique identifier, structured by `key` property.
156
+ * `update(entity)`: This method updates an existing entity in the data storage system, structured by `key` property.
157
+ * `delete(entity)`: This method deletes an entity from the data storage system based on its unique identifier, structured by `key` property.
158
+ * `list`: This method retrieves all elements of a data storage entity.
159
+
160
+ **Usage**
161
+ ---------
162
+
163
+ The `Repository` class can be used to create a repository for managing entities in a data storage system. For example:
164
+ ```javascript
165
+ const repository = new Repository(Entity, Context);
166
+ const entity = new Entity({ id: 'f47ac10b-58cc-43e8-9b9a-8f8f9f9f9f9f' }, { name: 'Cthulhu da Silva' });
167
+
168
+ await repository.create(entity);
169
+ //created {id:'f47ac10b-58cc-43e8-9b9a-8f8f9f9f9f9f', name:'Cthulhu da Silva'} in Entity table
170
+
171
+ const retrievedEntity = await repository.get(entity);
172
+ //retrieves {id:'f47ac10b-58cc-43e8-9b9a-8f8f9f9f9f9f', name:'Cthulhu da Silva'} from Entity table
173
+
174
+ retrievedEntity.changeName('Cthulhu de Souza')
175
+
176
+ await repository.update(entity);
177
+ //Now changes from 'Cthulhu da Silva' name to 'Cthulhu de Souza'
178
+
179
+ await repository.delete(entity);
180
+ //Removes {id:'f47ac10b-58cc-43e8-9b9a-8f8f9f9f9f9f', name:'Cthulhu de Souza'} from database
181
+
182
+ ```
183
+ Note that this is just a basic example, and you will need to customize the usage to fit your specific use case.
184
+
185
+ ### Context
186
+ ------------
187
+
188
+ The `Context` class represents a database context and is responsible for managing the database connection, executing queries, and handling transactions. It can be extended by two classes `ApiContext` and `DbContext`
189
+
190
+ ### Api Context ###
191
+ --------------------
192
+
193
+ The `ApiContext` class is defined as follows:
194
+ ```javascript
195
+ Class Context:
196
+ Fields:
197
+ #http
198
+ constructor(config = axios.create({})) {
199
+ this.#http = config;
200
+ }
201
+ Methods:
202
+ http()
203
+ ```
204
+ **Fields**
205
+ ----------
206
+
207
+ The `ApiContext` class has one private field:
208
+
209
+ * `#http`: This field represents the HTTP client instance used to execute queries.
210
+
211
+ **Constructor**
212
+ --------------
213
+
214
+ The `ApiContext` class has a constructor that takes an optional `config` parameter:
215
+
216
+ * `config`: This parameter is an object that configures the HTTP client instance using `axios`. If not provided, a default configuration is used.
217
+
218
+ The constructor initializes the `#http` field with the provided configuration.
219
+
220
+ **Methods**
221
+ ------------
222
+
223
+ The `ApiContext` class provides one method:
224
+
225
+ * `http()`: This method returns the HTTP client instance used to execute queries.
226
+
227
+ **Axios Configuration**
228
+ ----------------------
229
+
230
+ The `ApiContext` class uses the `axios` library to create an HTTP client instance. You can configure the HTTP client instance by passing a `config` object to the constructor.
231
+
232
+ For example:
233
+ ```javascript
234
+ const config = {
235
+ baseURL: 'https://api.example.com',
236
+ headers: {
237
+ 'Content-Type': 'application/json',
238
+ },
239
+ };
240
+ const context = new ApiContext(config);
241
+ ```
242
+
243
+ ### DbContext ###
244
+ ------------
245
+
246
+ The `DbContext` class is defined as follows:
247
+ ```javascript
248
+ Class DbContext:
249
+ Fields:
250
+ #db
251
+ constructor(database=knex()) {
252
+ this.#db = database
253
+ }
254
+ Methods:
255
+ db()
256
+ unitOfWork(...repositories)
257
+ terraform(models = [Model])
258
+ ```
259
+
260
+ **Constructor**
261
+ --------------
262
+
263
+ The `DbContext` class has a constructor that takes a `database` parameter:
264
+
265
+ * `database`: This parameter is Knex.js instance.
266
+
267
+ **Methods**
268
+ ------------
269
+
270
+ The `Context` class provides several methods:
271
+
272
+ * `db()`: This method returns the database instance used to execute queries.
273
+ * `unitOfWork(...repositories)`: This method creates a unit of work that can be used to execute multiple queries in a single transaction.
274
+ * `terraform(models = [Model])`: This method define initial state of database and execute table migrations.
275
+
276
+ **Knex.js Configuration**
277
+ -------------------------
278
+
279
+ The `DbContext` class uses the `knex` library to create a Knex.js instance. You can configure the Knex.js instance by passing a `knexConfig` instance to the constructor of `knex`.
280
+
281
+ For example:
282
+ ```javascript
283
+ const knexConfig = {
284
+ client: 'pg',
285
+ connection: 'postgresql://user:password@host:port/database',
286
+ migrations: {
287
+ directory: './migrations',
288
+ },
289
+ };
290
+
291
+ const database = knex(knexConfig)
292
+ const context = new DbContext(database);
293
+ ```
294
+ This configuration sets the database client, connection string, and migration directory for the Knex.js instance.
295
+
296
+ **UnitOfWork**
297
+ -------------
298
+
299
+ **Method Signature**
300
+ --------------------
301
+
302
+ `unitOfWork(...repositories)`
303
+
304
+ The `unitOfWork` method creates a unit of work that can be used to execute multiple queries in a single transaction. The method takes one or more `Repository` instances as arguments.
305
+
306
+ For example:
307
+ ```javascript
308
+ const userRepository = new UserRepository(context);
309
+ const orderRepository = new OrderRepository(context);
310
+ const tx = await context.unitOfWork(userRepository, orderRepository);
311
+ ```
312
+ **Parameters**
313
+ --------------
314
+
315
+ * `repositories`: A variable number of repository objects that will be used to execute queries within the unit of work.
316
+
317
+ **Return Type**
318
+ --------------
319
+
320
+ The `unitOfWork` method returns an object with two methods: `done` and `rollback`.
321
+
322
+
323
+ **Example Usage**
324
+ -----------------
325
+
326
+ ```javascript
327
+ const unitOfWork = context.unitOfWork(repo1, repo2);
328
+
329
+ // Execute queries on the repositories
330
+ repo1.create({ name: 'John Doe' });
331
+ repo2.create({ userId: 1, total: 100 });
332
+
333
+ // Commit the transaction
334
+ unitOfWork.done().then(() => {
335
+ console.log('Transaction committed');
336
+ });
337
+
338
+ // Alternatively, roll back the transaction
339
+ unitOfWork.rollback().then(() => {
340
+ console.log('Transaction rolled back');
341
+ });
342
+ ```
343
+ **Terraform**
344
+ -------------
345
+
346
+ The `terraform` method creates initial database state of every `Model` type passed by parameters and execute eventual database migrations. The method takes an array of types based on `Model` as an argument.
347
+
348
+ For example:
349
+ ```javascript
350
+
351
+ await context.terraform([UserModel, OrderModel]);
352
+ ```
353
+ This code creates initial state on database and execute database migrations for the `User` and `Order` tables.
354
+
package/index.js CHANGED
@@ -448,13 +448,13 @@ let Repository$1 = class Repository{
448
448
 
449
449
  set context(value=knex()){ this.myContext=()=>value(this.#name); }
450
450
 
451
- create = (entity = new Entity())=>
451
+ insert = (entity = new Entity())=>
452
452
  this.myContext()
453
453
  .insert(entity.$)
454
454
  .then(()=>new Result({data:entity}))
455
455
  .catch(err=>Promise.reject( new Result({code:500,message:err}) ))
456
456
 
457
- insert = (entity = new Entity())=>
457
+ create = (entity = new Entity())=>
458
458
  this.myContext()
459
459
  .insert(entity.data,Object.keys(entity.key))
460
460
  .then(ids=>new Result({
@@ -633,7 +633,7 @@ class DbLinked {
633
633
  this.#linking = link;
634
634
  }
635
635
 
636
- createBatch = (linkings=[ new Linking() ])=>
636
+ insertBatch = (linkings=[ new Linking() ])=>
637
637
  this.myContext()
638
638
  .insert(
639
639
  linkings.map(l => (
@@ -646,7 +646,7 @@ class DbLinked {
646
646
  .then(()=>new Result({data:linkings}))
647
647
  .catch(err=>Promise.reject(new Result({code:500,message:err})));
648
648
 
649
- create = (linked=new Linking())=>
649
+ insert = (linked=new Linking())=>
650
650
  this.myContext()
651
651
  .insert({
652
652
  ...linked.key,
@@ -662,7 +662,7 @@ class DbLinked {
662
662
  .build({abscissa,ordinate:o})
663
663
  );
664
664
 
665
- return this.createBatch(batch);
665
+ return this.insertBatch(batch);
666
666
  }
667
667
 
668
668
  insertAbscissasByOrdinate = ({ ordinate=new Entity(),abscissas=[new Entity()] })=>{
@@ -671,7 +671,7 @@ class DbLinked {
671
671
  .build({abscissa:a,ordinate})
672
672
  );
673
673
 
674
- return this.createBatch(batch)
674
+ return this.insertBatch(batch)
675
675
  }
676
676
 
677
677
  delete = (linked=new Linking())=>
@@ -771,7 +771,7 @@ let Context$1 = class Context{
771
771
  class Repository {
772
772
  #context
773
773
 
774
- constructor(entity=Entity,context=new Context$1()){
774
+ constructor(context=new Context$1()){
775
775
  this.#context = context;
776
776
  }
777
777
 
@@ -785,8 +785,8 @@ class Repository {
785
785
  }
786
786
 
787
787
  class RestfulRepository extends Repository{
788
- constructor(entity = Entity,context=new Context$1()){
789
- super(entity,context);
788
+ constructor(context=new Context$1()){
789
+ super(context);
790
790
  }
791
791
 
792
792
  #keysAsParams(route='',key={}){
@@ -815,13 +815,6 @@ class RestfulRepository extends Repository{
815
815
  : Promise.reject(new Result({code:value.status, message:value.statusText}));
816
816
  })
817
817
 
818
- list=(route='',query={}|undefined)=>this.#httpGet(route,query)
819
- .then(value=>{
820
- value.status === 200
821
- ? new Result({data:value.data})
822
- : Promise.reject(new Result({code:value.status, message:value.statusText}));
823
- })
824
-
825
818
  create=(route='',entity=new Entity())=>this.context
826
819
  .http
827
820
  .post(route,entity.data)
@@ -857,7 +850,7 @@ class RestfulRepository extends Repository{
857
850
  })
858
851
  }
859
852
 
860
- delete=(id,entity = new Entity())=>{
853
+ delete=(route='',entity = new Entity())=>{
861
854
  const url = this.#keysAsParams(route,entity.key);
862
855
 
863
856
  return this.context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keltoi/hydra",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Boilerplate to start your API from zero using Repository Pattern",
5
5
  "main": "index.js",
6
6
  "module": "index.js",