@apso/cli 0.1.0 → 0.1.5

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 (35) hide show
  1. package/dist/commands/server/scaffold.d.ts +1 -1
  2. package/dist/commands/server/scaffold.js +5 -5
  3. package/dist/lib/apsorc-parser.d.ts +27 -1
  4. package/dist/lib/apsorc-parser.js +97 -3
  5. package/dist/lib/controller.d.ts +8 -0
  6. package/dist/lib/controller.js +24 -9
  7. package/dist/lib/dto.d.ts +14 -7
  8. package/dist/lib/dto.js +61 -25
  9. package/dist/lib/entity.d.ts +9 -0
  10. package/dist/lib/entity.js +14 -4
  11. package/dist/lib/gql-dto.js +1 -1
  12. package/dist/lib/index-module.d.ts +11 -0
  13. package/dist/lib/index-module.js +11 -0
  14. package/dist/lib/module.d.ts +13 -1
  15. package/dist/lib/module.js +26 -10
  16. package/dist/lib/service.d.ts +9 -1
  17. package/dist/lib/service.js +22 -7
  18. package/dist/lib/templates/entities/entity-col-json.eta +1 -1
  19. package/dist/lib/templates/rest/dto-rest.eta +2 -2
  20. package/dist/lib/templates/rest/module-rest.eta +1 -1
  21. package/dist/lib/templates/rest/service-rest-spec.eta +38 -0
  22. package/dist/lib/utils/casing.d.ts +2 -1
  23. package/dist/lib/utils/casing.js +18 -8
  24. package/dist/lib/utils/field.js +1 -1
  25. package/dist/lib/utils/relationships/parse.d.ts +45 -1
  26. package/dist/lib/utils/relationships/parse.js +228 -79
  27. package/dist/lib/utils/relationships/parse.spec.d.ts +1 -0
  28. package/dist/lib/utils/relationships/parse.spec.js +229 -0
  29. package/dist/test-nested-relationships.d.ts +1 -0
  30. package/dist/test-nested-relationships.js +112 -0
  31. package/dist/tests/templates/entities/types/json-template.spec.js +2 -2
  32. package/dist/tests/utils/relationship-deduplication.spec.d.ts +1 -0
  33. package/dist/tests/utils/relationship-deduplication.spec.js +98 -0
  34. package/oclif.manifest.json +1 -1
  35. package/package.json +3 -1
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const parse_1 = require("./parse");
5
+ (0, globals_1.describe)('parseRelationships', () => {
6
+ // Mock console.warn to prevent test output clutter
7
+ (0, globals_1.beforeEach)(() => {
8
+ globals_1.jest.spyOn(console, 'warn').mockImplementation(() => { });
9
+ });
10
+ (0, globals_1.afterEach)(() => {
11
+ globals_1.jest.restoreAllMocks();
12
+ });
13
+ (0, globals_1.describe)('ManyToMany Relationships', () => {
14
+ (0, globals_1.it)('should parse a single bidirectional ManyToMany definition', () => {
15
+ const relationships = [
16
+ {
17
+ from: 'EntityA',
18
+ to: 'EntityB',
19
+ type: 'ManyToMany',
20
+ // eslint-disable-next-line camelcase
21
+ bi_directional: true,
22
+ },
23
+ ];
24
+ const expected = {
25
+ EntityA: [
26
+ {
27
+ name: 'EntityB',
28
+ type: 'ManyToMany',
29
+ referenceName: 'EntityB',
30
+ join: true,
31
+ biDirectional: true,
32
+ joinTableName: undefined,
33
+ joinColumnName: undefined,
34
+ inverseJoinColumnName: undefined,
35
+ inverseReferenceName: 'EntityA', // PascalCase entity name
36
+ },
37
+ ],
38
+ EntityB: [
39
+ {
40
+ name: 'EntityA',
41
+ type: 'ManyToMany',
42
+ referenceName: 'EntityA',
43
+ join: false,
44
+ biDirectional: true,
45
+ joinTableName: undefined,
46
+ joinColumnName: undefined,
47
+ inverseJoinColumnName: undefined,
48
+ inverseReferenceName: 'EntityB', // PascalCase entity name
49
+ },
50
+ ],
51
+ };
52
+ const result = (0, parse_1.parseRelationships)(relationships);
53
+ (0, globals_1.expect)(result).toEqual(expected);
54
+ });
55
+ (0, globals_1.it)('should parse a single bidirectional ManyToMany with to_name', () => {
56
+ const relationships = [
57
+ {
58
+ from: 'User',
59
+ to: 'Group',
60
+ type: 'ManyToMany',
61
+ // eslint-disable-next-line camelcase
62
+ bi_directional: true,
63
+ // eslint-disable-next-line camelcase
64
+ to_name: 'userGroups',
65
+ },
66
+ ];
67
+ const expected = {
68
+ User: [
69
+ {
70
+ name: 'Group',
71
+ type: 'ManyToMany',
72
+ referenceName: 'userGroups',
73
+ join: true,
74
+ biDirectional: true,
75
+ joinTableName: undefined,
76
+ joinColumnName: undefined,
77
+ inverseJoinColumnName: undefined,
78
+ inverseReferenceName: 'User', // PascalCase entity name
79
+ },
80
+ ],
81
+ Group: [
82
+ {
83
+ name: 'User',
84
+ type: 'ManyToMany',
85
+ referenceName: 'User',
86
+ join: false,
87
+ biDirectional: true,
88
+ joinTableName: undefined,
89
+ joinColumnName: undefined,
90
+ inverseJoinColumnName: undefined,
91
+ inverseReferenceName: 'userGroups', // Preserved camelCase from to_name
92
+ },
93
+ ],
94
+ };
95
+ const result = (0, parse_1.parseRelationships)(relationships);
96
+ (0, globals_1.expect)(result).toEqual(expected);
97
+ });
98
+ (0, globals_1.it)('should parse a unidirectional ManyToMany definition', () => {
99
+ const relationships = [
100
+ {
101
+ from: 'Post',
102
+ to: 'Tag',
103
+ type: 'ManyToMany',
104
+ // bi_directional is false or omitted
105
+ },
106
+ ];
107
+ const expected = {
108
+ Post: [
109
+ {
110
+ name: 'Tag',
111
+ type: 'ManyToMany',
112
+ referenceName: 'Tag',
113
+ join: true,
114
+ biDirectional: false,
115
+ joinTableName: undefined,
116
+ joinColumnName: undefined,
117
+ inverseJoinColumnName: undefined,
118
+ inverseReferenceName: undefined, // Correct: No inverse name for unidirectional
119
+ },
120
+ ],
121
+ // No entry expected for Tag
122
+ };
123
+ const result = (0, parse_1.parseRelationships)(relationships);
124
+ (0, globals_1.expect)(result).toEqual(expected);
125
+ });
126
+ (0, globals_1.it)('should handle join table details correctly', () => {
127
+ const relationships = [
128
+ {
129
+ from: 'Student',
130
+ to: 'Course',
131
+ type: 'ManyToMany',
132
+ // eslint-disable-next-line camelcase
133
+ bi_directional: true,
134
+ // UNUSED: eslint-disable-next-line camelcase
135
+ joinTableName: 'enrollments',
136
+ // UNUSED: eslint-disable-next-line camelcase
137
+ joinColumnName: 'student_id',
138
+ // UNUSED: eslint-disable-next-line camelcase
139
+ inverseJoinColumnName: 'course_id',
140
+ },
141
+ ];
142
+ const expected = {
143
+ Student: [
144
+ {
145
+ name: 'Course',
146
+ type: 'ManyToMany',
147
+ referenceName: 'Course',
148
+ join: true,
149
+ biDirectional: true,
150
+ joinTableName: 'enrollments',
151
+ joinColumnName: 'student_id',
152
+ inverseJoinColumnName: 'course_id',
153
+ inverseReferenceName: 'Student', // PascalCase entity name
154
+ },
155
+ ],
156
+ Course: [
157
+ {
158
+ name: 'Student',
159
+ type: 'ManyToMany',
160
+ referenceName: 'Student',
161
+ join: false,
162
+ biDirectional: true,
163
+ joinTableName: undefined,
164
+ joinColumnName: undefined,
165
+ inverseJoinColumnName: undefined,
166
+ inverseReferenceName: 'Course', // PascalCase entity name
167
+ },
168
+ ],
169
+ };
170
+ const result = (0, parse_1.parseRelationships)(relationships);
171
+ (0, globals_1.expect)(result).toEqual(expected);
172
+ });
173
+ });
174
+ (0, globals_1.describe)('Mixed Relationship Types', () => {
175
+ (0, globals_1.it)('should correctly parse ManyToMany alongside other types', () => {
176
+ const relationships = [
177
+ {
178
+ from: 'User',
179
+ to: 'Profile',
180
+ type: 'OneToOne',
181
+ // eslint-disable-next-line camelcase
182
+ bi_directional: true,
183
+ // eslint-disable-next-line camelcase
184
+ to_name: 'userProfile'
185
+ },
186
+ {
187
+ from: 'User',
188
+ to: 'Post',
189
+ type: 'OneToMany',
190
+ // eslint-disable-next-line camelcase
191
+ bi_directional: true // Implies ManyToOne on Post
192
+ },
193
+ {
194
+ from: 'Role',
195
+ to: 'User',
196
+ type: 'ManyToMany',
197
+ // eslint-disable-next-line camelcase
198
+ bi_directional: true
199
+ }
200
+ ];
201
+ const result = (0, parse_1.parseRelationships)(relationships);
202
+ // Check User relationships
203
+ (0, globals_1.expect)(result.User).toHaveLength(3);
204
+ (0, globals_1.expect)(result.User).toContainEqual(globals_1.expect.objectContaining({ name: 'Profile', type: 'OneToOne', referenceName: 'userProfile', biDirectional: true, join: false })); // Expect preserved camelCase
205
+ (0, globals_1.expect)(result.User).toContainEqual(globals_1.expect.objectContaining({ name: 'Post', type: 'OneToMany', referenceName: 'Post', biDirectional: true })); // PascalCase entity name
206
+ (0, globals_1.expect)(result.User).toContainEqual(globals_1.expect.objectContaining({ name: 'Role', type: 'ManyToMany', referenceName: 'Role', biDirectional: true, join: false })); // PascalCase entity name
207
+ // Check Profile relationship
208
+ (0, globals_1.expect)(result.Profile).toHaveLength(1);
209
+ (0, globals_1.expect)(result.Profile).toContainEqual(globals_1.expect.objectContaining({
210
+ name: 'User',
211
+ type: 'OneToOne',
212
+ referenceName: 'User',
213
+ biDirectional: true,
214
+ join: true
215
+ }));
216
+ // Check Post relationship
217
+ (0, globals_1.expect)(result.Post).toHaveLength(1);
218
+ (0, globals_1.expect)(result.Post).toContainEqual(globals_1.expect.objectContaining({
219
+ name: 'User',
220
+ type: 'ManyToOne',
221
+ referenceName: 'User',
222
+ biDirectional: true
223
+ }));
224
+ // Check Role relationship
225
+ (0, globals_1.expect)(result.Role).toHaveLength(1);
226
+ (0, globals_1.expect)(result.Role).toContainEqual(globals_1.expect.objectContaining({ name: 'User', type: 'ManyToMany', referenceName: 'User', biDirectional: true, join: true })); // PascalCase entity name
227
+ });
228
+ });
229
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const relationships_1 = require("./lib/utils/relationships");
4
+ // Test case based on the diff showing camelCase issues
5
+ const relationshipMap = {
6
+ User: [
7
+ {
8
+ type: "ManyToOne",
9
+ name: "Workspace",
10
+ },
11
+ ],
12
+ Workspace: [
13
+ {
14
+ type: "OneToMany",
15
+ name: "User",
16
+ },
17
+ {
18
+ type: "OneToMany",
19
+ name: "WorkspaceUser",
20
+ },
21
+ ],
22
+ WorkspaceUser: [
23
+ {
24
+ type: "ManyToOne",
25
+ name: "Workspace",
26
+ },
27
+ {
28
+ type: "ManyToOne",
29
+ name: "User",
30
+ },
31
+ {
32
+ type: "OneToMany",
33
+ name: "WorkspaceServiceUser",
34
+ },
35
+ ],
36
+ WorkspaceServiceUser: [
37
+ {
38
+ type: "ManyToOne",
39
+ name: "WorkspaceUser",
40
+ },
41
+ {
42
+ type: "ManyToOne",
43
+ name: "WorkspaceService",
44
+ }
45
+ ],
46
+ WorkspaceService: [
47
+ {
48
+ type: "OneToMany",
49
+ name: "WorkspaceServiceUser",
50
+ },
51
+ {
52
+ type: "OneToMany",
53
+ name: "WorkspaceServiceApiKey",
54
+ },
55
+ ],
56
+ WorkspaceServiceApiKey: [
57
+ {
58
+ type: "ManyToOne",
59
+ name: "WorkspaceService",
60
+ }
61
+ ]
62
+ };
63
+ // Run the test for User entity
64
+ console.log("Nested relationships for User:");
65
+ const userNestedRelationships = (0, relationships_1.getNestedRelationships)("User", relationshipMap);
66
+ userNestedRelationships.forEach(rel => console.log(` - ${rel}`));
67
+ // Run test for CustomerCard entity (similar to what's shown in the diff)
68
+ // Create a CustomerCard example with the pattern seen in the diff
69
+ const customerCardRelationshipMap = {
70
+ CustomerCard: [
71
+ {
72
+ type: "ManyToOne",
73
+ name: "User",
74
+ }
75
+ ],
76
+ User: [
77
+ {
78
+ type: "OneToMany",
79
+ name: "Workspace",
80
+ },
81
+ ],
82
+ Workspace: [
83
+ {
84
+ type: "OneToMany",
85
+ name: "WorkspaceUser",
86
+ },
87
+ {
88
+ type: "OneToMany",
89
+ name: "WorkspaceService",
90
+ }
91
+ ],
92
+ WorkspaceService: [
93
+ {
94
+ type: "OneToMany",
95
+ name: "WorkspaceServiceApiKey",
96
+ },
97
+ ],
98
+ WorkspaceServiceApiKey: [
99
+ {
100
+ type: "ManyToOne",
101
+ name: "WorkspaceService",
102
+ }
103
+ ]
104
+ };
105
+ console.log("\nNested relationships for CustomerCard:");
106
+ const customerCardNestedRelationships = (0, relationships_1.getNestedRelationships)("CustomerCard", customerCardRelationshipMap);
107
+ customerCardNestedRelationships.forEach(rel => console.log(` - ${rel}`));
108
+ // Expected to see properly camelCased names like:
109
+ // - workspace.workspaceUsers
110
+ // - workspace.workspaceUsers.workspaceServiceUsers
111
+ // - workspace.workspaceUsers.workspaceServiceUsers.workspaceService
112
+ // - workspace.workspaceUsers.workspaceServiceUsers.workspaceService.workspaceServiceApiKeys
@@ -17,7 +17,7 @@ const globals_1 = require("@jest/globals");
17
17
  nullable: false
18
18
  },
19
19
  assertions: [
20
- { type: 'contains', text: "@Column('json'" },
20
+ { type: 'contains', text: "@Column('jsonb'" },
21
21
  { type: 'contains', text: 'nullable: false' },
22
22
  { type: 'contains', text: 'jsonData: JSON;' }
23
23
  ]
@@ -30,7 +30,7 @@ const globals_1 = require("@jest/globals");
30
30
  nullable: true
31
31
  },
32
32
  assertions: [
33
- { type: 'contains', text: "@Column('json'" },
33
+ { type: 'contains', text: "@Column('jsonb'" },
34
34
  { type: 'contains', text: 'nullable: true' },
35
35
  { type: 'contains', text: 'optionalJsonData: JSON;' }
36
36
  ]
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const parse_1 = require("../../lib/utils/relationships/parse");
5
+ (0, globals_1.describe)("getRelationshipForTemplate deduplication", () => {
6
+ (0, globals_1.test)("should deduplicate ManyToMany relationships", () => {
7
+ // Mock entity name
8
+ const entityName = "CustomerCard";
9
+ // Mock relationships - simulating bidirectional ManyToMany where
10
+ // both sides are added to the relationships array for the same entity
11
+ const mockRelationships = [
12
+ // First ManyToMany relationship (non-join side)
13
+ {
14
+ name: "Subscription",
15
+ type: "ManyToMany",
16
+ referenceName: "subscriptions",
17
+ biDirectional: true,
18
+ join: false,
19
+ inverseReferenceName: "customercards"
20
+ },
21
+ // Duplicate relationship (join side) - similar to what happens in bidirectional ManyToMany
22
+ {
23
+ name: "Subscription",
24
+ type: "ManyToMany",
25
+ referenceName: "subscriptions",
26
+ biDirectional: true,
27
+ join: true,
28
+ inverseReferenceName: "customercards"
29
+ }
30
+ ];
31
+ // Run the function
32
+ const result = (0, parse_1.getRelationshipForTemplate)(entityName, mockRelationships);
33
+ // Assertions
34
+ // 1. Should only have one relationship
35
+ (0, globals_1.expect)(result.length).toBe(1);
36
+ // 2. The remaining relationship should be the one with join=true
37
+ (0, globals_1.expect)(result[0].name).toBe("Subscription");
38
+ (0, globals_1.expect)(result[0].joinTable).toBe(true);
39
+ });
40
+ (0, globals_1.test)("should keep first relationship when reference names differ", () => {
41
+ // Mock entity name
42
+ const entityName = "User";
43
+ // Create mocked relationships with same target but different reference names
44
+ const mockRelationships = [
45
+ {
46
+ name: "Workspace",
47
+ type: "ManyToMany",
48
+ referenceName: "workspaces",
49
+ biDirectional: true,
50
+ join: true
51
+ },
52
+ {
53
+ name: "Workspace",
54
+ type: "ManyToMany",
55
+ referenceName: "ownedWorkspaces",
56
+ biDirectional: true,
57
+ join: true
58
+ }
59
+ ];
60
+ // Run the function
61
+ const result = (0, parse_1.getRelationshipForTemplate)(entityName, mockRelationships);
62
+ // Assertions
63
+ // New behavior: Both relationships with different referenceName should be kept
64
+ (0, globals_1.expect)(result.length).toBe(2);
65
+ const referenceNames = result.map(r => r.relationshipName || r.referenceName);
66
+ (0, globals_1.expect)(referenceNames).toContain("workspaces");
67
+ (0, globals_1.expect)(referenceNames).toContain("ownedWorkspaces");
68
+ });
69
+ (0, globals_1.test)("should prioritize owner side (join=true) when deduplicating", () => {
70
+ // Scenario based on real issue: two ManyToMany relationships to same entity
71
+ const entityName = "Tag";
72
+ // Create relationships where one has join=true and one has join=false (non-owning side)
73
+ const mockRelationships = [
74
+ {
75
+ name: "WorkspaceService",
76
+ type: "ManyToMany",
77
+ biDirectional: true,
78
+ join: false,
79
+ referenceName: "workspaceservices"
80
+ },
81
+ {
82
+ name: "WorkspaceService",
83
+ type: "ManyToMany",
84
+ biDirectional: true,
85
+ join: true,
86
+ referenceName: "workspaceservices"
87
+ }
88
+ ];
89
+ // Run the function
90
+ const result = (0, parse_1.getRelationshipForTemplate)(entityName, mockRelationships);
91
+ // Assertions
92
+ // Should only have one relationship
93
+ (0, globals_1.expect)(result.length).toBe(1);
94
+ // Should be the one with join=true
95
+ (0, globals_1.expect)(result[0].name).toBe("WorkspaceService");
96
+ (0, globals_1.expect)(result[0].joinTable).toBe(true); // joinTable corresponds to join
97
+ });
98
+ });
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0",
2
+ "version": "0.1.5",
3
3
  "commands": {
4
4
  "server:new": {
5
5
  "id": "server:new",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.5",
4
4
  "description": "Apso CLI",
5
5
  "author": "Apso by Mavric - @mavric",
6
6
  "bin": {
@@ -56,6 +56,7 @@
56
56
  "ts-jest": "^29.1.0",
57
57
  "ts-node": "^10.9.1",
58
58
  "tslib": "^2.5.0",
59
+ "typedoc": "^0.28.3",
59
60
  "typescript": "^5"
60
61
  },
61
62
  "oclif": {
@@ -86,6 +87,7 @@
86
87
  "test": "jest",
87
88
  "test:watch": "jest --watch",
88
89
  "test:cov": "jest --coverage",
90
+ "docs:generate": "typedoc src/lib/index.ts --out docs",
89
91
  "version": "oclif readme && git add README.md"
90
92
  },
91
93
  "engines": {