@twin.org/entity-storage-service 0.0.3-next.2 → 0.0.3-next.21

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/docs/examples.md CHANGED
@@ -1 +1,77 @@
1
- # @twin.org/entity-storage-service - Examples
1
+ # Entity Storage Service Examples
2
+
3
+ Use these snippets to wire a registered connector into the service layer and to expose matching REST routes.
4
+
5
+ ## EntityStorageService
6
+
7
+ ```typescript
8
+ import {
9
+ EntityStorageService,
10
+ generateRestRoutesEntityStorage
11
+ } from '@twin.org/entity-storage-service';
12
+ import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
13
+ import { MemoryEntityStorageConnector } from '@twin.org/entity-storage-connector-memory';
14
+ import {
15
+ ComparisonOperator,
16
+ LogicalOperator,
17
+ SortDirection,
18
+ type EntityCondition
19
+ } from '@twin.org/entity';
20
+
21
+ interface Profile {
22
+ id: string;
23
+ email: string;
24
+ status: 'active' | 'inactive';
25
+ createdAt: string;
26
+ }
27
+
28
+ EntityStorageConnectorFactory.register(
29
+ 'profile-storage',
30
+ () =>
31
+ new MemoryEntityStorageConnector<Profile>({
32
+ entitySchema: 'Profile'
33
+ })
34
+ );
35
+
36
+ const service = new EntityStorageService<Profile>({
37
+ entityStorageType: 'profile-storage'
38
+ });
39
+
40
+ const className = service.className();
41
+
42
+ await service.set({
43
+ id: 'profile-1',
44
+ email: 'ada@example.com',
45
+ status: 'active',
46
+ createdAt: '2026-03-09T10:30:00.000Z'
47
+ });
48
+
49
+ const byPrimaryKey = await service.get('profile-1');
50
+ const bySecondaryIndex = await service.get('ada@example.com', 'email');
51
+
52
+ const activeCondition: EntityCondition<Profile> = {
53
+ logicalOperator: LogicalOperator.And,
54
+ conditions: [
55
+ {
56
+ property: 'status',
57
+ comparison: ComparisonOperator.Equals,
58
+ value: 'active'
59
+ }
60
+ ]
61
+ };
62
+
63
+ const result = await service.query(
64
+ activeCondition,
65
+ 'createdAt',
66
+ SortDirection.Descending,
67
+ ['id', 'email', 'status'],
68
+ undefined,
69
+ 25
70
+ );
71
+
72
+ await service.remove('profile-1');
73
+
74
+ const routes = generateRestRoutesEntityStorage('/profiles', 'profileStorageComponent', {
75
+ typeName: 'Profile'
76
+ });
77
+ ```
@@ -28,6 +28,17 @@
28
28
  "tags": [
29
29
  "EntityStorage"
30
30
  ],
31
+ "parameters": [
32
+ {
33
+ "name": "conditions",
34
+ "description": "The optional conditions to match for the entity, JSON encoded array of property/value pairs.",
35
+ "in": "query",
36
+ "required": false,
37
+ "schema": {
38
+ "type": "string"
39
+ }
40
+ }
41
+ ],
31
42
  "security": [
32
43
  {
33
44
  "jwtBearerAuthScheme": []
@@ -115,6 +126,91 @@
115
126
  }
116
127
  }
117
128
  },
129
+ "delete": {
130
+ "operationId": "entityStorageEmpty",
131
+ "summary": "Remove all entries from entity storage.",
132
+ "tags": [
133
+ "EntityStorage"
134
+ ],
135
+ "security": [
136
+ {
137
+ "jwtBearerAuthScheme": []
138
+ }
139
+ ],
140
+ "requestBody": {
141
+ "description": "Remove all entries from entity storage.",
142
+ "required": true,
143
+ "content": {
144
+ "text/plain": {
145
+ "schema": {
146
+ "$ref": "#/components/schemas/EntityStorageEmptyRequest"
147
+ }
148
+ }
149
+ }
150
+ },
151
+ "responses": {
152
+ "204": {
153
+ "description": "The rest request ended in success with no data."
154
+ },
155
+ "400": {
156
+ "description": "The server cannot process the request, see the content for more details.",
157
+ "content": {
158
+ "application/json": {
159
+ "schema": {
160
+ "$ref": "#/components/schemas/Error"
161
+ },
162
+ "examples": {
163
+ "exampleResponse": {
164
+ "value": {
165
+ "name": "GeneralError",
166
+ "message": "errorMessage",
167
+ "properties": {
168
+ "foo": "bar"
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ },
176
+ "401": {
177
+ "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.",
178
+ "content": {
179
+ "application/json": {
180
+ "schema": {
181
+ "$ref": "#/components/schemas/Error"
182
+ },
183
+ "examples": {
184
+ "exampleResponse": {
185
+ "value": {
186
+ "name": "UnauthorizedError",
187
+ "message": "errorMessage"
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ },
194
+ "500": {
195
+ "description": "The server has encountered a situation it does not know how to handle, see the content for more details.",
196
+ "content": {
197
+ "application/json": {
198
+ "schema": {
199
+ "$ref": "#/components/schemas/Error"
200
+ },
201
+ "examples": {
202
+ "exampleResponse": {
203
+ "value": {
204
+ "name": "InternalServerError",
205
+ "message": "errorMessage"
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ },
118
214
  "get": {
119
215
  "operationId": "entityStorageList",
120
216
  "summary": "Query entries from entity storage.",
@@ -265,6 +361,200 @@
265
361
  }
266
362
  }
267
363
  },
364
+ "/entity-storage/batch": {
365
+ "post": {
366
+ "operationId": "entityStorageSetBatch",
367
+ "summary": "Set multiple entries in entity storage.",
368
+ "tags": [
369
+ "EntityStorage"
370
+ ],
371
+ "security": [
372
+ {
373
+ "jwtBearerAuthScheme": []
374
+ }
375
+ ],
376
+ "requestBody": {
377
+ "description": "Set multiple entries in entity storage.",
378
+ "required": true,
379
+ "content": {
380
+ "application/json": {
381
+ "schema": {
382
+ "$ref": "#/components/schemas/EntityStorageSetBatchRequest"
383
+ },
384
+ "examples": {
385
+ "entityStorageSetBatchRequestExample": {
386
+ "value": [
387
+ {
388
+ "id": "12345",
389
+ "name": "My Item"
390
+ },
391
+ {
392
+ "id": "67890",
393
+ "name": "My Other Item"
394
+ }
395
+ ]
396
+ }
397
+ }
398
+ }
399
+ }
400
+ },
401
+ "responses": {
402
+ "204": {
403
+ "description": "The rest request ended in success with no data."
404
+ },
405
+ "400": {
406
+ "description": "The server cannot process the request, see the content for more details.",
407
+ "content": {
408
+ "application/json": {
409
+ "schema": {
410
+ "$ref": "#/components/schemas/Error"
411
+ },
412
+ "examples": {
413
+ "exampleResponse": {
414
+ "value": {
415
+ "name": "GeneralError",
416
+ "message": "errorMessage",
417
+ "properties": {
418
+ "foo": "bar"
419
+ }
420
+ }
421
+ }
422
+ }
423
+ }
424
+ }
425
+ },
426
+ "401": {
427
+ "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.",
428
+ "content": {
429
+ "application/json": {
430
+ "schema": {
431
+ "$ref": "#/components/schemas/Error"
432
+ },
433
+ "examples": {
434
+ "exampleResponse": {
435
+ "value": {
436
+ "name": "UnauthorizedError",
437
+ "message": "errorMessage"
438
+ }
439
+ }
440
+ }
441
+ }
442
+ }
443
+ },
444
+ "500": {
445
+ "description": "The server has encountered a situation it does not know how to handle, see the content for more details.",
446
+ "content": {
447
+ "application/json": {
448
+ "schema": {
449
+ "$ref": "#/components/schemas/Error"
450
+ },
451
+ "examples": {
452
+ "exampleResponse": {
453
+ "value": {
454
+ "name": "InternalServerError",
455
+ "message": "errorMessage"
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+ }
462
+ }
463
+ },
464
+ "delete": {
465
+ "operationId": "entityStorageRemoveBatch",
466
+ "summary": "Remove multiple entries from entity storage by id.",
467
+ "tags": [
468
+ "EntityStorage"
469
+ ],
470
+ "security": [
471
+ {
472
+ "jwtBearerAuthScheme": []
473
+ }
474
+ ],
475
+ "requestBody": {
476
+ "description": "Remove multiple entries from entity storage by id.",
477
+ "required": true,
478
+ "content": {
479
+ "application/json": {
480
+ "schema": {
481
+ "$ref": "#/components/schemas/EntityStorageRemoveBatchRequest"
482
+ },
483
+ "examples": {
484
+ "entityStorageRemoveBatchRequestExample": {
485
+ "value": [
486
+ "12345",
487
+ "67890"
488
+ ]
489
+ }
490
+ }
491
+ }
492
+ }
493
+ },
494
+ "responses": {
495
+ "204": {
496
+ "description": "The rest request ended in success with no data."
497
+ },
498
+ "400": {
499
+ "description": "The server cannot process the request, see the content for more details.",
500
+ "content": {
501
+ "application/json": {
502
+ "schema": {
503
+ "$ref": "#/components/schemas/Error"
504
+ },
505
+ "examples": {
506
+ "exampleResponse": {
507
+ "value": {
508
+ "name": "GeneralError",
509
+ "message": "errorMessage",
510
+ "properties": {
511
+ "foo": "bar"
512
+ }
513
+ }
514
+ }
515
+ }
516
+ }
517
+ }
518
+ },
519
+ "401": {
520
+ "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.",
521
+ "content": {
522
+ "application/json": {
523
+ "schema": {
524
+ "$ref": "#/components/schemas/Error"
525
+ },
526
+ "examples": {
527
+ "exampleResponse": {
528
+ "value": {
529
+ "name": "UnauthorizedError",
530
+ "message": "errorMessage"
531
+ }
532
+ }
533
+ }
534
+ }
535
+ }
536
+ },
537
+ "500": {
538
+ "description": "The server has encountered a situation it does not know how to handle, see the content for more details.",
539
+ "content": {
540
+ "application/json": {
541
+ "schema": {
542
+ "$ref": "#/components/schemas/Error"
543
+ },
544
+ "examples": {
545
+ "exampleResponse": {
546
+ "value": {
547
+ "name": "InternalServerError",
548
+ "message": "errorMessage"
549
+ }
550
+ }
551
+ }
552
+ }
553
+ }
554
+ }
555
+ }
556
+ }
557
+ },
268
558
  "/entity-storage/{id}": {
269
559
  "get": {
270
560
  "operationId": "entityStorageGet",
@@ -292,6 +582,15 @@
292
582
  "schema": {
293
583
  "type": "string"
294
584
  }
585
+ },
586
+ {
587
+ "name": "conditions",
588
+ "description": "The optional conditions to match for the entity, JSON encoded array of property/value pairs.",
589
+ "in": "query",
590
+ "required": false,
591
+ "schema": {
592
+ "type": "string"
593
+ }
295
594
  }
296
595
  ],
297
596
  "security": [
@@ -415,6 +714,15 @@
415
714
  },
416
715
  "style": "simple",
417
716
  "example": "12345"
717
+ },
718
+ {
719
+ "name": "conditions",
720
+ "description": "The optional conditions to match for the entity, JSON encoded array of property/value pairs.",
721
+ "in": "query",
722
+ "required": false,
723
+ "schema": {
724
+ "type": "string"
725
+ }
418
726
  }
419
727
  ],
420
728
  "security": [
@@ -506,10 +814,128 @@
506
814
  }
507
815
  }
508
816
  }
817
+ },
818
+ "/entity-storage/count": {
819
+ "get": {
820
+ "operationId": "entityStorageCount",
821
+ "summary": "Count entries in entity storage.",
822
+ "tags": [
823
+ "EntityStorage"
824
+ ],
825
+ "parameters": [
826
+ {
827
+ "name": "conditions",
828
+ "description": "The optional conditions to filter the count, JSON encoded EntityCondition.",
829
+ "in": "query",
830
+ "required": false,
831
+ "schema": {
832
+ "type": "string"
833
+ }
834
+ }
835
+ ],
836
+ "security": [
837
+ {
838
+ "jwtBearerAuthScheme": []
839
+ }
840
+ ],
841
+ "responses": {
842
+ "200": {
843
+ "description": "The response for counting entries in entity storage.",
844
+ "content": {
845
+ "application/json": {
846
+ "schema": {
847
+ "$ref": "#/components/schemas/EntityStorageCountResponse"
848
+ },
849
+ "examples": {
850
+ "entityStorageCountResponseExample": {
851
+ "value": {
852
+ "count": 1
853
+ }
854
+ }
855
+ }
856
+ }
857
+ }
858
+ },
859
+ "400": {
860
+ "description": "The server cannot process the request, see the content for more details.",
861
+ "content": {
862
+ "application/json": {
863
+ "schema": {
864
+ "$ref": "#/components/schemas/Error"
865
+ },
866
+ "examples": {
867
+ "exampleResponse": {
868
+ "value": {
869
+ "name": "GeneralError",
870
+ "message": "errorMessage",
871
+ "properties": {
872
+ "foo": "bar"
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+ }
879
+ },
880
+ "401": {
881
+ "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.",
882
+ "content": {
883
+ "application/json": {
884
+ "schema": {
885
+ "$ref": "#/components/schemas/Error"
886
+ },
887
+ "examples": {
888
+ "exampleResponse": {
889
+ "value": {
890
+ "name": "UnauthorizedError",
891
+ "message": "errorMessage"
892
+ }
893
+ }
894
+ }
895
+ }
896
+ }
897
+ },
898
+ "500": {
899
+ "description": "The server has encountered a situation it does not know how to handle, see the content for more details.",
900
+ "content": {
901
+ "application/json": {
902
+ "schema": {
903
+ "$ref": "#/components/schemas/Error"
904
+ },
905
+ "examples": {
906
+ "exampleResponse": {
907
+ "value": {
908
+ "name": "InternalServerError",
909
+ "message": "errorMessage"
910
+ }
911
+ }
912
+ }
913
+ }
914
+ }
915
+ }
916
+ }
917
+ }
509
918
  }
510
919
  },
511
920
  "components": {
512
921
  "schemas": {
922
+ "EntityStorageCountResponse": {
923
+ "type": "object",
924
+ "properties": {
925
+ "count": {
926
+ "type": "number",
927
+ "description": "The total count of entities."
928
+ }
929
+ },
930
+ "required": [
931
+ "count"
932
+ ],
933
+ "description": "The body of the response."
934
+ },
935
+ "EntityStorageEmptyRequest": {
936
+ "description": "Remove all entries from entity storage.",
937
+ "type": "object"
938
+ },
513
939
  "EntityStorageGetResponse": {
514
940
  "description": "The data for the requested entity."
515
941
  },
@@ -529,13 +955,25 @@
529
955
  "required": [
530
956
  "entities"
531
957
  ],
532
- "additionalProperties": false,
533
958
  "description": "The list of entries from the query."
534
959
  },
960
+ "EntityStorageRemoveBatchRequest": {
961
+ "type": "array",
962
+ "items": {
963
+ "type": "string"
964
+ },
965
+ "description": "The ids of the entities to remove."
966
+ },
967
+ "EntityStorageSetBatchRequest": {
968
+ "type": "array",
969
+ "items": {},
970
+ "description": "The entities to set."
971
+ },
535
972
  "EntityStorageSetRequest": {
536
973
  "description": "The data to be used in the entity."
537
974
  },
538
975
  "Error": {
976
+ "description": "Model to describe serialized error.",
539
977
  "type": "object",
540
978
  "properties": {
541
979
  "name": {
@@ -544,7 +982,7 @@
544
982
  },
545
983
  "message": {
546
984
  "type": "string",
547
- "description": "The message for the error."
985
+ "description": "The message for the error as an i18n key."
548
986
  },
549
987
  "source": {
550
988
  "type": "string",
@@ -566,63 +1004,35 @@
566
1004
  "required": [
567
1005
  "name",
568
1006
  "message"
569
- ],
570
- "additionalProperties": false,
571
- "description": "Model to describe serialized error."
1007
+ ]
572
1008
  },
573
1009
  "NotFoundResponse": {
574
1010
  "type": "object",
575
- "additionalProperties": false,
576
1011
  "properties": {
577
1012
  "notFoundId": {
578
1013
  "type": "string",
579
1014
  "description": "The id if the item that was not found."
580
- },
581
- "name": {
582
- "type": "string",
583
- "description": "The name for the error."
584
- },
585
- "message": {
586
- "type": "string",
587
- "description": "The message for the error."
588
- },
589
- "source": {
590
- "type": "string",
591
- "description": "The source of the error."
592
- },
593
- "properties": {
594
- "type": "object",
595
- "additionalProperties": {},
596
- "description": "Any additional information for the error."
597
- },
598
- "stack": {
599
- "type": "string",
600
- "description": "The stack trace for the error."
601
- },
602
- "cause": {
603
- "$ref": "#/components/schemas/Error"
604
1015
  }
605
1016
  },
606
- "required": [
607
- "message",
608
- "name"
1017
+ "allOf": [
1018
+ {
1019
+ "$ref": "#/components/schemas/Error"
1020
+ }
609
1021
  ],
610
1022
  "description": "The body which contains the error."
611
1023
  },
612
1024
  "SortDirection": {
1025
+ "description": "The sort directions.",
613
1026
  "anyOf": [
614
1027
  {
615
- "type": "string",
616
1028
  "const": "asc",
617
1029
  "description": "Ascending."
618
1030
  },
619
1031
  {
620
- "type": "string",
621
1032
  "const": "desc",
622
1033
  "description": "Descending."
623
1034
  }
624
- ],
625
- "description": "The sort directions."
1035
+ ]
626
1036
  }
627
1037
  },
628
1038
  "securitySchemes": {