@apso/cli 0.1.9 → 0.3.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 +511 -0
- package/dist/commands/server/scaffold.js +1 -1
- package/dist/lib/controller.js +2 -2
- package/dist/lib/dto.js +1 -1
- package/dist/lib/entity.js +1 -1
- package/dist/lib/service.d.ts +1 -8
- package/dist/lib/service.js +28 -4
- package/dist/lib/templates/entities/entity-col-datetime.eta +4 -0
- package/dist/lib/templates/entities/entity-col-geography.eta +29 -0
- package/dist/lib/templates/entities/entity-col-geometry.eta +29 -0
- package/dist/lib/templates/entities/entity-col-geometrycollection.eta +29 -0
- package/dist/lib/templates/entities/entity-col-linestring.eta +37 -0
- package/dist/lib/templates/entities/entity-col-multilinestring.eta +44 -0
- package/dist/lib/templates/entities/entity-col-multipoint.eta +37 -0
- package/dist/lib/templates/entities/entity-col-multipolygon.eta +50 -0
- package/dist/lib/templates/entities/entity-col-polygon.eta +44 -0
- package/dist/lib/templates/graphql/dto/dto-col-decimal.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-geography.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-geometry.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-geometrycollection.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-linestring.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-multilinestring.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-multipoint.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-multipolygon.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-numeric.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-point.eta +4 -0
- package/dist/lib/templates/graphql/dto/dto-col-polygon.eta +4 -0
- package/dist/lib/templates/header.eta +8 -3
- package/dist/lib/templates/rest/dto-rest.eta +26 -1
- package/dist/lib/templates/rest/ormconfig.eta +2 -0
- package/dist/lib/templates/rest/service-rest-spec.eta +16 -7
- package/dist/lib/types/field.d.ts +3 -1
- package/dist/lib/utils/field.d.ts +1 -0
- package/dist/lib/utils/field.js +42 -9
- package/dist/lib/utils/file-system.d.ts +4 -0
- package/dist/lib/utils/file-system.js +9 -1
- package/dist/lib/utils/relationships/parse.js +1 -1
- package/dist/tests/templates/entities/types/decimal-field-validation.spec.d.ts +1 -0
- package/dist/tests/templates/entities/types/decimal-field-validation.spec.js +100 -0
- package/dist/tests/templates/entities/types/geometry-template.spec.d.ts +1 -0
- package/dist/tests/templates/entities/types/geometry-template.spec.js +58 -0
- package/dist/tests/templates/entities/types/polygon-template.spec.d.ts +1 -0
- package/dist/tests/templates/entities/types/polygon-template.spec.js +58 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "geometry",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (geometry: any) => {
|
|
12
|
+
if (!geometry) return null;
|
|
13
|
+
return geometry;
|
|
14
|
+
},
|
|
15
|
+
from: (pgGeometry: any) => {
|
|
16
|
+
if (!pgGeometry) return null;
|
|
17
|
+
return pgGeometry;
|
|
18
|
+
}
|
|
19
|
+
}<% if (field.nullable) {%>,
|
|
20
|
+
nullable: true<% } %>
|
|
21
|
+
})
|
|
22
|
+
<% if (field.index ) {%>
|
|
23
|
+
@Index()
|
|
24
|
+
<% } %>
|
|
25
|
+
<% if (field.primary ) {%>
|
|
26
|
+
@PrimaryColumn()
|
|
27
|
+
<% } %>
|
|
28
|
+
|
|
29
|
+
<%= field.name %>: any;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "geometrycollection",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (geometrycollection: { geometries: Array<any> } | null) => {
|
|
12
|
+
if (!geometrycollection) return null;
|
|
13
|
+
return geometrycollection;
|
|
14
|
+
},
|
|
15
|
+
from: (pgGeometryCollection: any) => {
|
|
16
|
+
if (!pgGeometryCollection) return null;
|
|
17
|
+
return pgGeometryCollection;
|
|
18
|
+
}
|
|
19
|
+
}<% if (field.nullable) {%>,
|
|
20
|
+
nullable: true<% } %>
|
|
21
|
+
})
|
|
22
|
+
<% if (field.index ) {%>
|
|
23
|
+
@Index()
|
|
24
|
+
<% } %>
|
|
25
|
+
<% if (field.primary ) {%>
|
|
26
|
+
@PrimaryColumn()
|
|
27
|
+
<% } %>
|
|
28
|
+
|
|
29
|
+
<%= field.name %>: { geometries: Array<any> };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "linestring",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (linestring: { coordinates: Array<{x: number, y: number}> } | null) => {
|
|
12
|
+
if (!linestring) return null;
|
|
13
|
+
const coords = linestring.coordinates.map(coord => `${coord.x} ${coord.y}`).join(',');
|
|
14
|
+
return `LINESTRING(${coords})`;
|
|
15
|
+
},
|
|
16
|
+
from: (pgLinestring: string | null) => {
|
|
17
|
+
if (!pgLinestring) return null;
|
|
18
|
+
// Extract coordinates from LINESTRING(x1 y1,x2 y2,...) format
|
|
19
|
+
const match = pgLinestring.match(/LINESTRING\((.+)\)/);
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
const coords = match[1].split(',').map(coord => {
|
|
22
|
+
const [x, y] = coord.trim().split(' ');
|
|
23
|
+
return { x: parseFloat(x), y: parseFloat(y) };
|
|
24
|
+
});
|
|
25
|
+
return { coordinates: coords };
|
|
26
|
+
}
|
|
27
|
+
}<% if (field.nullable) {%>,
|
|
28
|
+
nullable: true<% } %>
|
|
29
|
+
})
|
|
30
|
+
<% if (field.index ) {%>
|
|
31
|
+
@Index()
|
|
32
|
+
<% } %>
|
|
33
|
+
<% if (field.primary ) {%>
|
|
34
|
+
@PrimaryColumn()
|
|
35
|
+
<% } %>
|
|
36
|
+
|
|
37
|
+
<%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "multilinestring",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (multilinestring: { coordinates: Array<Array<{x: number, y: number}>> } | null) => {
|
|
12
|
+
if (!multilinestring) return null;
|
|
13
|
+
const linestrings = multilinestring.coordinates.map(linestring => {
|
|
14
|
+
const coords = linestring.map(coord => `${coord.x} ${coord.y}`).join(',');
|
|
15
|
+
return `(${coords})`;
|
|
16
|
+
});
|
|
17
|
+
return `MULTILINESTRING(${linestrings.join(',')})`;
|
|
18
|
+
},
|
|
19
|
+
from: (pgMultilinestring: string | null) => {
|
|
20
|
+
if (!pgMultilinestring) return null;
|
|
21
|
+
// Extract coordinates from MULTILINESTRING((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)) format
|
|
22
|
+
const match = pgMultilinestring.match(/MULTILINESTRING\((.+)\)/);
|
|
23
|
+
if (!match) return null;
|
|
24
|
+
const linestrings = match[1].split('),(').map(linestring => {
|
|
25
|
+
const cleanLinestring = linestring.replace(/[()]/g, '');
|
|
26
|
+
const coords = cleanLinestring.split(',').map(coord => {
|
|
27
|
+
const [x, y] = coord.trim().split(' ');
|
|
28
|
+
return { x: parseFloat(x), y: parseFloat(y) };
|
|
29
|
+
});
|
|
30
|
+
return coords;
|
|
31
|
+
});
|
|
32
|
+
return { coordinates: linestrings };
|
|
33
|
+
}
|
|
34
|
+
}<% if (field.nullable) {%>,
|
|
35
|
+
nullable: true<% } %>
|
|
36
|
+
})
|
|
37
|
+
<% if (field.index ) {%>
|
|
38
|
+
@Index()
|
|
39
|
+
<% } %>
|
|
40
|
+
<% if (field.primary ) {%>
|
|
41
|
+
@PrimaryColumn()
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
44
|
+
<%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "multipoint",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (multipoint: { coordinates: Array<{x: number, y: number}> } | null) => {
|
|
12
|
+
if (!multipoint) return null;
|
|
13
|
+
const coords = multipoint.coordinates.map(coord => `${coord.x} ${coord.y}`).join(',');
|
|
14
|
+
return `MULTIPOINT(${coords})`;
|
|
15
|
+
},
|
|
16
|
+
from: (pgMultipoint: string | null) => {
|
|
17
|
+
if (!pgMultipoint) return null;
|
|
18
|
+
// Extract coordinates from MULTIPOINT(x1 y1,x2 y2,...) format
|
|
19
|
+
const match = pgMultipoint.match(/MULTIPOINT\((.+)\)/);
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
const coords = match[1].split(',').map(coord => {
|
|
22
|
+
const [x, y] = coord.trim().split(' ');
|
|
23
|
+
return { x: parseFloat(x), y: parseFloat(y) };
|
|
24
|
+
});
|
|
25
|
+
return { coordinates: coords };
|
|
26
|
+
}
|
|
27
|
+
}<% if (field.nullable) {%>,
|
|
28
|
+
nullable: true<% } %>
|
|
29
|
+
})
|
|
30
|
+
<% if (field.index ) {%>
|
|
31
|
+
@Index()
|
|
32
|
+
<% } %>
|
|
33
|
+
<% if (field.primary ) {%>
|
|
34
|
+
@PrimaryColumn()
|
|
35
|
+
<% } %>
|
|
36
|
+
|
|
37
|
+
<%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "multipolygon",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (multipolygon: { coordinates: Array<Array<Array<{x: number, y: number}>>> } | null) => {
|
|
12
|
+
if (!multipolygon) return null;
|
|
13
|
+
const polygons = multipolygon.coordinates.map(polygon => {
|
|
14
|
+
const rings = polygon.map(ring => {
|
|
15
|
+
const coords = ring.map(coord => `${coord.x} ${coord.y}`).join(',');
|
|
16
|
+
return `(${coords})`;
|
|
17
|
+
});
|
|
18
|
+
return `(${rings.join(',')})`;
|
|
19
|
+
});
|
|
20
|
+
return `MULTIPOLYGON(${polygons.join(',')})`;
|
|
21
|
+
},
|
|
22
|
+
from: (pgMultipolygon: string | null) => {
|
|
23
|
+
if (!pgMultipolygon) return null;
|
|
24
|
+
// Extract coordinates from MULTIPOLYGON(((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)),((x1 y1,x2 y2,...),(x1 y1,x2 y2,...))) format
|
|
25
|
+
const match = pgMultipolygon.match(/MULTIPOLYGON\((.+)\)/);
|
|
26
|
+
if (!match) return null;
|
|
27
|
+
const polygons = match[1].split(')),((').map(polygon => {
|
|
28
|
+
const cleanPolygon = polygon.replace(/[()]/g, '');
|
|
29
|
+
const rings = cleanPolygon.split('),(').map(ring => {
|
|
30
|
+
const coords = ring.split(',').map(coord => {
|
|
31
|
+
const [x, y] = coord.trim().split(' ');
|
|
32
|
+
return { x: parseFloat(x), y: parseFloat(y) };
|
|
33
|
+
});
|
|
34
|
+
return coords;
|
|
35
|
+
});
|
|
36
|
+
return rings;
|
|
37
|
+
});
|
|
38
|
+
return { coordinates: polygons };
|
|
39
|
+
}
|
|
40
|
+
}<% if (field.nullable) {%>,
|
|
41
|
+
nullable: true<% } %>
|
|
42
|
+
})
|
|
43
|
+
<% if (field.index ) {%>
|
|
44
|
+
@Index()
|
|
45
|
+
<% } %>
|
|
46
|
+
<% if (field.primary ) {%>
|
|
47
|
+
@PrimaryColumn()
|
|
48
|
+
<% } %>
|
|
49
|
+
|
|
50
|
+
<%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<% const {field} = it; %>
|
|
2
|
+
|
|
3
|
+
@IsOptional({ groups: [UPDATE] })
|
|
4
|
+
<% if (!field.nullable && !field.auto) { %>
|
|
5
|
+
@IsNotEmpty({ groups: [CREATE] })
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
@Column({
|
|
9
|
+
"type": "polygon",
|
|
10
|
+
transformer: {
|
|
11
|
+
to: (polygon: { coordinates: Array<Array<{x: number, y: number}>> } | null) => {
|
|
12
|
+
if (!polygon) return null;
|
|
13
|
+
const rings = polygon.coordinates.map(ring => {
|
|
14
|
+
const coords = ring.map(coord => `${coord.x} ${coord.y}`).join(',');
|
|
15
|
+
return `(${coords})`;
|
|
16
|
+
});
|
|
17
|
+
return `POLYGON(${rings.join(',')})`;
|
|
18
|
+
},
|
|
19
|
+
from: (pgPolygon: string | null) => {
|
|
20
|
+
if (!pgPolygon) return null;
|
|
21
|
+
// Extract coordinates from POLYGON((x1 y1,x2 y2,...),(x1 y1,x2 y2,...)) format
|
|
22
|
+
const match = pgPolygon.match(/POLYGON\((.+)\)/);
|
|
23
|
+
if (!match) return null;
|
|
24
|
+
const rings = match[1].split('),(').map(ring => {
|
|
25
|
+
const cleanRing = ring.replace(/[()]/g, '');
|
|
26
|
+
const coords = cleanRing.split(',').map(coord => {
|
|
27
|
+
const [x, y] = coord.trim().split(' ');
|
|
28
|
+
return { x: parseFloat(x), y: parseFloat(y) };
|
|
29
|
+
});
|
|
30
|
+
return coords;
|
|
31
|
+
});
|
|
32
|
+
return { coordinates: rings };
|
|
33
|
+
}
|
|
34
|
+
}<% if (field.nullable) {%>,
|
|
35
|
+
nullable: true<% } %>
|
|
36
|
+
})
|
|
37
|
+
<% if (field.index ) {%>
|
|
38
|
+
@Index()
|
|
39
|
+
<% } %>
|
|
40
|
+
<% if (field.primary ) {%>
|
|
41
|
+
@PrimaryColumn()
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
44
|
+
<%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ApiProperty } from '@nestjs/swagger';
|
|
2
1
|
<%~ includeFile('./header.eta') %>
|
|
3
2
|
|
|
3
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
4
|
+
|
|
4
5
|
<% if (it.importEnums) { %>
|
|
5
6
|
import * as enums from '../../enums';
|
|
6
7
|
<% } %>
|
|
@@ -14,6 +15,18 @@ export class <%= it.entityName %>Create {
|
|
|
14
15
|
<%= field.name %>: JSON;
|
|
15
16
|
<%} else if (field.type === 'timestamp' || field.type === 'timestamptz') {%>
|
|
16
17
|
<%= field.name %>: Date;
|
|
18
|
+
<%} else if (field.type === 'geometry' || field.type === 'geography') {%>
|
|
19
|
+
<%= field.name %>: any;
|
|
20
|
+
<%} else if (field.type === 'point') {%>
|
|
21
|
+
<%= field.name %>: { x: number, y: number };
|
|
22
|
+
<%} else if (field.type === 'linestring' || field.type === 'multipoint') {%>
|
|
23
|
+
<%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
|
|
24
|
+
<%} else if (field.type === 'polygon' || field.type === 'multilinestring') {%>
|
|
25
|
+
<%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
|
|
26
|
+
<%} else if (field.type === 'multipolygon') {%>
|
|
27
|
+
<%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
|
|
28
|
+
<%} else if (field.type === 'geometrycollection') {%>
|
|
29
|
+
<%= field.name %>: { geometries: Array<any> };
|
|
17
30
|
<%} else {%>
|
|
18
31
|
<%= field.name %>: <%= field.dataType %>;
|
|
19
32
|
<% } %>
|
|
@@ -33,6 +46,18 @@ export class <%= it.entityName %> {
|
|
|
33
46
|
<%= field.name %>: JSON;
|
|
34
47
|
<%} else if (field.type === 'timestamp' || field.type === 'timestamptz') {%>
|
|
35
48
|
<%= field.name %>: Date;
|
|
49
|
+
<%} else if (field.type === 'geometry' || field.type === 'geography') {%>
|
|
50
|
+
<%= field.name %>: any;
|
|
51
|
+
<%} else if (field.type === 'point') {%>
|
|
52
|
+
<%= field.name %>: { x: number, y: number };
|
|
53
|
+
<%} else if (field.type === 'linestring' || field.type === 'multipoint') {%>
|
|
54
|
+
<%= field.name %>: { coordinates: Array<{ x: number, y: number }> };
|
|
55
|
+
<%} else if (field.type === 'polygon' || field.type === 'multilinestring') {%>
|
|
56
|
+
<%= field.name %>: { coordinates: Array<Array<{ x: number, y: number }>> };
|
|
57
|
+
<%} else if (field.type === 'multipolygon') {%>
|
|
58
|
+
<%= field.name %>: { coordinates: Array<Array<Array<{ x: number, y: number }>>> };
|
|
59
|
+
<%} else if (field.type === 'geometrycollection') {%>
|
|
60
|
+
<%= field.name %>: { geometries: Array<any> };
|
|
36
61
|
<%} else {%>
|
|
37
62
|
<%= field.name %>: <%= field.dataType %>;
|
|
38
63
|
<% } %>
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
<%~ includeFile('./header.eta') %>
|
|
2
|
+
|
|
1
3
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
2
4
|
import { <%= it.svcName %> } from './<%= it.entityName %>.service';
|
|
3
|
-
import {
|
|
5
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
4
6
|
import { <%= it.entityName %> } from './<%= it.entityName %>.entity';
|
|
7
|
+
<% it.allRelatedEntities.forEach(function(entity) { if (entity !== it.entityName) { %>
|
|
8
|
+
import { <%= entity %> } from '../<%= entity %>/<%= entity %>.entity';
|
|
9
|
+
<% }}); %>
|
|
10
|
+
|
|
11
|
+
// All related entities (including the main entity) are included below to ensure TypeORM can resolve relationships for this service's entity and its graph.
|
|
5
12
|
|
|
6
13
|
describe('<%= it.svcName %>', () => {
|
|
7
14
|
let service: <%= it.svcName %>;
|
|
@@ -18,13 +25,15 @@ describe('<%= it.svcName %>', () => {
|
|
|
18
25
|
};
|
|
19
26
|
|
|
20
27
|
const module: TestingModule = await Test.createTestingModule({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
28
|
+
imports: [
|
|
29
|
+
TypeOrmModule.forRoot({
|
|
30
|
+
// ...database config (provide your test DB config here)
|
|
31
|
+
entities: [<%= [it.entityName, ...it.allRelatedEntities.filter(e => e !== it.entityName)].join(', ') %>],
|
|
32
|
+
synchronize: true,
|
|
33
|
+
}),
|
|
34
|
+
TypeOrmModule.forFeature([<%= it.entityName %>]),
|
|
27
35
|
],
|
|
36
|
+
providers: [<%= it.svcName %>],
|
|
28
37
|
}).compile();
|
|
29
38
|
|
|
30
39
|
service = module.get<<%= it.svcName %>>(<%= it.svcName %>);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FieldType = "text" | "boolean" | "array" | "enum" | "integer" | "float" | "date" | "json" | "json-plain";
|
|
1
|
+
export type FieldType = "text" | "boolean" | "array" | "enum" | "integer" | "float" | "decimal" | "numeric" | "date" | "json" | "json-plain";
|
|
2
2
|
export interface Field {
|
|
3
3
|
name: string;
|
|
4
4
|
type: FieldType;
|
|
@@ -9,6 +9,8 @@ export interface Field {
|
|
|
9
9
|
unique?: boolean;
|
|
10
10
|
default?: string | null;
|
|
11
11
|
length?: number;
|
|
12
|
+
precision?: number;
|
|
13
|
+
scale?: number;
|
|
12
14
|
is_email?: boolean;
|
|
13
15
|
}
|
|
14
16
|
export interface ComputedField extends Field {
|
|
@@ -4,3 +4,4 @@ export declare const getDefaultValueForField: (field: Field) => string | null;
|
|
|
4
4
|
export declare const getFieldForTemplate: (fields: Field[], entityName: string) => ComputedField[];
|
|
5
5
|
export declare const fieldToEnumType: (fieldName: string, entityName: string) => string;
|
|
6
6
|
export declare const typeExistsInEntity: (entity: Entity, type: string) => number;
|
|
7
|
+
export declare const validateDecimalField: (field: Field) => void;
|
package/dist/lib/utils/field.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.typeExistsInEntity = exports.fieldToEnumType = exports.getFieldForTemplate = exports.getDefaultValueForField = exports.getJsTypeFromFieldType = void 0;
|
|
3
|
+
exports.validateDecimalField = exports.typeExistsInEntity = exports.fieldToEnumType = exports.getFieldForTemplate = exports.getDefaultValueForField = exports.getJsTypeFromFieldType = void 0;
|
|
4
4
|
const casing_1 = require("./casing");
|
|
5
5
|
const typeToJsType = {
|
|
6
6
|
array: "string[]",
|
|
@@ -36,7 +36,16 @@ const typeToJsType = {
|
|
|
36
36
|
bytea: "Buffer",
|
|
37
37
|
point: "{ x: number, y: number }",
|
|
38
38
|
int4range: "{ lower: number, upper: number, lowerInclusive: boolean, upperInclusive: boolean }",
|
|
39
|
-
enum: "string",
|
|
39
|
+
enum: "string",
|
|
40
|
+
// PostGIS data types
|
|
41
|
+
geometry: "any",
|
|
42
|
+
geography: "any",
|
|
43
|
+
linestring: "{ coordinates: Array<{ x: number, y: number }> }",
|
|
44
|
+
polygon: "{ coordinates: Array<Array<{ x: number, y: number }>> }",
|
|
45
|
+
multipoint: "{ coordinates: Array<{ x: number, y: number }> }",
|
|
46
|
+
multilinestring: "{ coordinates: Array<Array<{ x: number, y: number }>> }",
|
|
47
|
+
multipolygon: "{ coordinates: Array<Array<Array<{ x: number, y: number }>>> }",
|
|
48
|
+
geometrycollection: "{ geometries: Array<any> }",
|
|
40
49
|
};
|
|
41
50
|
const getJsTypeFromFieldType = (type) => { var _a; return (_a = typeToJsType[type]) !== null && _a !== void 0 ? _a : "string"; };
|
|
42
51
|
exports.getJsTypeFromFieldType = getJsTypeFromFieldType;
|
|
@@ -70,13 +79,17 @@ const getDefaultValueForField = (field) => {
|
|
|
70
79
|
}
|
|
71
80
|
};
|
|
72
81
|
exports.getDefaultValueForField = getDefaultValueForField;
|
|
73
|
-
const getFieldForTemplate = (fields, entityName) => fields.map((field) =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
: (0, exports.
|
|
79
|
-
|
|
82
|
+
const getFieldForTemplate = (fields, entityName) => fields.map((field) => {
|
|
83
|
+
// Validate decimal/numeric fields
|
|
84
|
+
(0, exports.validateDecimalField)(field);
|
|
85
|
+
return {
|
|
86
|
+
...field,
|
|
87
|
+
default: (0, exports.getDefaultValueForField)(field),
|
|
88
|
+
dataType: field.type === "enum"
|
|
89
|
+
? (0, exports.fieldToEnumType)(field.name, entityName)
|
|
90
|
+
: (0, exports.getJsTypeFromFieldType)(field.type),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
80
93
|
exports.getFieldForTemplate = getFieldForTemplate;
|
|
81
94
|
const fieldToEnumType = (fieldName, entityName) => `${(0, casing_1.pascalCase)(entityName)}${(0, casing_1.pascalCase)(fieldName)}Enum`;
|
|
82
95
|
exports.fieldToEnumType = fieldToEnumType;
|
|
@@ -84,3 +97,23 @@ const typeExistsInEntity = (entity, type) => (entity === null || entity === void
|
|
|
84
97
|
? entity === null || entity === void 0 ? void 0 : entity.fields.findIndex((field) => field.type === type)
|
|
85
98
|
: -1;
|
|
86
99
|
exports.typeExistsInEntity = typeExistsInEntity;
|
|
100
|
+
const validateDecimalField = (field) => {
|
|
101
|
+
if (field.type !== "decimal" && field.type !== "numeric") {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Validate precision
|
|
105
|
+
if (field.precision !== undefined && (field.precision < 1 || field.precision > 131072)) {
|
|
106
|
+
throw new Error(`${field.name}: Invalid precision value ${field.precision}. Must be between 1 and 131072.`);
|
|
107
|
+
}
|
|
108
|
+
// Validate scale
|
|
109
|
+
if (field.scale !== undefined) {
|
|
110
|
+
if (field.scale < 0 || field.scale > 16383) {
|
|
111
|
+
throw new Error(`${field.name}: Invalid scale value ${field.scale}. Must be between 0 and 16383.`);
|
|
112
|
+
}
|
|
113
|
+
// Validate that scale doesn't exceed precision
|
|
114
|
+
if (field.precision !== undefined && field.scale > field.precision) {
|
|
115
|
+
throw new Error(`${field.name}: Scale (${field.scale}) cannot be greater than precision (${field.precision}).`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
exports.validateDecimalField = validateDecimalField;
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export declare const createFile: (file: string, content: string) => Promise<void>;
|
|
2
2
|
export declare const createDirectoryContents: (templatePath: string, newProjectPath: string, apiType: string) => void;
|
|
3
|
+
export declare function withGeneratedMeta<T extends object>(data: T): T & {
|
|
4
|
+
generatedAt: string;
|
|
5
|
+
generatedBy: string;
|
|
6
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDirectoryContents = exports.createFile = void 0;
|
|
3
|
+
exports.withGeneratedMeta = exports.createDirectoryContents = exports.createFile = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const path = tslib_1.__importStar(require("path"));
|
|
6
6
|
const fs = tslib_1.__importStar(require("fs"));
|
|
@@ -50,3 +50,11 @@ const writeFile = (fileName, origFilePath, writePath, mode, apiType) => {
|
|
|
50
50
|
fs.chmodSync(fileWritePath, mode);
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
function withGeneratedMeta(data) {
|
|
54
|
+
return {
|
|
55
|
+
...data,
|
|
56
|
+
generatedAt: new Date().toISOString(),
|
|
57
|
+
generatedBy: process.env.USER || process.env.USERNAME || 'unknown',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
exports.withGeneratedMeta = withGeneratedMeta;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getRelationshipsForImport = exports.streamNestedRelationships = exports.getNestedRelationships = exports.generateNestedRelationships = exports.getRelationshipForTemplate = exports.getRelationshipIdField = exports.parseRelationships = exports.parseRelationship = exports.parseManyToMany = exports.parseManytoOne = exports.parseOneToMany = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const pluralize_1 = tslib_1.__importDefault(require("pluralize"));
|
|
6
|
-
const casing_1 = require("
|
|
6
|
+
const casing_1 = require("../casing");
|
|
7
7
|
/**
|
|
8
8
|
* Parses a OneToMany relationship definition.
|
|
9
9
|
* These are treated as bidirectional from the .apsorc definition.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|