Skip to content

Commit c3d5663

Browse files
authored
Merge pull request #41 from Mites-G/uuid-relationships
feat(json-api-nestjs): Uuid support for relationships
2 parents c88e1fd + a1d192b commit c3d5663

File tree

9 files changed

+211
-85
lines changed

9 files changed

+211
-85
lines changed

libs/json-api-nestjs/src/lib/factory/ajv/ajv.factory.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,34 @@ describe('AJV factory', () => {
228228
required: ['type', 'id'],
229229
});
230230

231+
expect(
232+
dataProperty['relationships']['properties']['notes']['properties'][
233+
'data'
234+
]['type']
235+
).toBe('array');
236+
expect(
237+
dataProperty['relationships']['properties']['notes']['properties'][
238+
'data'
239+
]['items']
240+
).toEqual({
241+
type: 'object',
242+
properties: {
243+
id: {
244+
type: 'string',
245+
description: 'Use string should be as uuid string',
246+
pattern:
247+
'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
248+
maxLength: 36,
249+
minLength: 36,
250+
},
251+
type: {
252+
type: 'string',
253+
enum: ['notes'],
254+
},
255+
},
256+
required: ['type', 'id'],
257+
});
258+
231259
expect(Object.keys(dataProperty['relationships']['properties'])).toEqual(
232260
relationField
233261
);

libs/json-api-nestjs/src/lib/factory/ajv/ajv.factory.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ export function AjvCallFactory(
2626

2727
for (const entity of options.entities) {
2828
const arrayProps: { [key: string]: boolean } = {};
29+
const uuidProps: { [key: string]: boolean } = {};
2930
const relationArrayProps: { [key: string]: { [key: string]: boolean } } =
3031
{};
32+
const relationUuids: { [key: string]: { [key: string]: boolean } } = {};
3133
const repository = dataSource.getRepository(entity);
3234
const relations = repository.metadata.relations.map((i) => {
3335
return i.propertyName;
@@ -36,6 +38,7 @@ export function AjvCallFactory(
3638
.filter((i) => !relations.includes(i.propertyName))
3739
.map((i) => {
3840
arrayProps[i.propertyName] = i.isArray;
41+
uuidProps[i.propertyName] = i.generationStrategy === 'uuid';
3942
return i.propertyName;
4043
});
4144
const relationType = repository.metadata.relations.reduce((acum, i) => {
@@ -63,6 +66,15 @@ export function AjvCallFactory(
6366
relationArrayProps[item.propertyName] =
6467
relationArrayProps[item.propertyName] || {};
6568
relationArrayProps[item.propertyName][i.propertyName] = i.isArray;
69+
70+
relationUuids[item.propertyName] =
71+
relationUuids[item.propertyName] || {};
72+
73+
if (i.isPrimary) {
74+
relationUuids[item.propertyName][i.propertyName] =
75+
i.generationStrategy === 'uuid';
76+
}
77+
6678
return i.propertyName;
6779
});
6880
const fakeObject = columns.reduce<Record<string, string>>(
@@ -110,6 +122,7 @@ export function AjvCallFactory(
110122
arrayProps,
111123
relationArrayProps,
112124
relationType,
125+
relationUuids,
113126
}
114127
),
115128
`inputBodyPostSchema-${schemaName}`
@@ -122,8 +135,10 @@ export function AjvCallFactory(
122135
`inputBodyPatchSchema-${schemaName}`,
123136
{
124137
arrayProps,
138+
uuidProps,
125139
relationArrayProps,
126140
relationType,
141+
relationUuids,
127142
}
128143
),
129144
`inputBodyPatchSchema-${schemaName}`

libs/json-api-nestjs/src/lib/factory/ajv/utils/input-body-patch-schema.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ export function inputBodyPatchSchema(
88
schemaName: string,
99
arrayPropsConfig: {
1010
arrayProps: { [key: string]: boolean };
11+
uuidProps: { [key: string]: boolean };
1112
relationArrayProps: { [key: string]: { [key: string]: boolean } };
1213
relationType: {
1314
[key: string]: Function | string;
1415
};
16+
relationUuids: { [key: string]: { [key: string]: boolean } };
1517
}
1618
): ReturnType<typeof inputBodyPostSchema> {
1719
const json = inputBodyPostSchema(
@@ -21,13 +23,18 @@ export function inputBodyPatchSchema(
2123
schemaName,
2224
arrayPropsConfig
2325
);
24-
const patternObject: Record<string, string> = {};
25-
if (
26-
Reflect.getMetadata('design:type', entity['prototype'], 'id') === Number
27-
) {
28-
patternObject.pattern = '^\\d+$';
29-
patternObject.description = 'Use string should be as number string';
26+
const patternObject: Record<string, string | number> = {};
27+
patternObject.pattern = '^\\d+$';
28+
patternObject.description = 'Use string should be as number string';
29+
30+
if (arrayPropsConfig.uuidProps.id) {
31+
patternObject.pattern =
32+
'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$';
33+
patternObject.maxLength = 36;
34+
patternObject.minLength = 36;
35+
patternObject.description = 'Use string should be as uuid string';
3036
}
37+
3138
json.properties.data.properties = {
3239
...{
3340
id: {

libs/json-api-nestjs/src/lib/factory/ajv/utils/input-body-post-schema.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function inputBodyPostSchema(
1313
relationType: {
1414
[key: string]: Function | string;
1515
};
16+
relationUuids: { [key: string]: { [key: string]: boolean } };
1617
}
1718
): typeof inputBodySchemaJson {
1819
const json: typeof inputBodySchemaJson = JSON.parse(
@@ -23,7 +24,7 @@ export function inputBodyPostSchema(
2324
camelToKebab(getEntityName(entity))
2425
);
2526

26-
const relDataType = {
27+
const baseRelDataType: Record<string, any> = {
2728
type: 'object',
2829
properties: {
2930
data: {
@@ -103,13 +104,44 @@ export function inputBodyPostSchema(
103104
...attributes,
104105
};
105106

107+
const uuidRelations = arrayPropsConfig.relationUuids || {};
106108
const relationships = Object.keys(relationsField).reduce((acum, item) => {
109+
const relDataType = {
110+
[item]: {
111+
...baseRelDataType,
112+
properties: {
113+
...baseRelDataType.properties,
114+
data: {
115+
...baseRelDataType.properties.data,
116+
properties: {
117+
...baseRelDataType.properties.data.properties,
118+
id: {
119+
...baseRelDataType.properties.data.properties.id,
120+
pattern: uuidRelations[item]?.id
121+
? '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
122+
: '^\\d+$',
123+
...(uuidRelations[item]?.id
124+
? {
125+
minLength: 36,
126+
maxLength: 36,
127+
}
128+
: {}),
129+
description: uuidRelations[item]?.id
130+
? 'Use string should be as uuid string'
131+
: 'Use string should be as number string',
132+
},
133+
},
134+
},
135+
},
136+
},
137+
};
138+
107139
const resultSchema = {
108-
...relDataType.properties.data,
140+
...relDataType[item].properties.data,
109141
properties: {
110-
...relDataType.properties.data.properties,
142+
...relDataType[item].properties.data.properties,
111143
type: {
112-
...relDataType.properties.data.properties.type,
144+
...relDataType[item].properties.data.properties.type,
113145
...(arrayPropsConfig.relationType[item]
114146
? {
115147
enum: [
@@ -124,9 +156,9 @@ export function inputBodyPostSchema(
124156
};
125157

126158
acum[item] = {
127-
...relDataType,
159+
...relDataType[item],
128160
properties: {
129-
...relDataType.properties,
161+
...relDataType[item].properties,
130162
data:
131163
Reflect.getMetadata('design:type', entity['prototype'], item) ===
132164
Array
@@ -138,6 +170,7 @@ export function inputBodyPostSchema(
138170
: resultSchema,
139171
},
140172
};
173+
141174
return acum;
142175
}, {});
143176

libs/json-api-nestjs/src/lib/mock-utils/db-for-test

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ SET xmloption = content;
1616
SET client_min_messages = warning;
1717
SET row_security = off;
1818

19+
create extension "uuid-ossp";
20+
1921
--
2022
-- Name: comment_kind_enum; Type: TYPE; Schema: public; Owner: -
2123
--
@@ -103,33 +105,14 @@ ALTER SEQUENCE public.comments_id_seq OWNED BY public.comments.id;
103105
--
104106

105107
CREATE TABLE public.notes (
106-
id integer NOT NULL,
108+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
107109
text text NOT NULL,
108110
created_by integer,
109111
created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
110112
updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP
111113
);
112114

113115

114-
--
115-
-- Name: notes_id_seq; Type: SEQUENCE; Schema: public; Owner: -
116-
--
117-
118-
CREATE SEQUENCE public.notes_id_seq
119-
AS integer
120-
START WITH 1
121-
INCREMENT BY 1
122-
NO MINVALUE
123-
NO MAXVALUE
124-
CACHE 1;
125-
126-
127-
--
128-
-- Name: notes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
129-
--
130-
131-
ALTER SEQUENCE public.notes_id_seq OWNED BY public.notes.id;
132-
133116

134117
--
135118
-- Name: migrations; Type: TABLE; Schema: public; Owner: -
@@ -421,13 +404,6 @@ ALTER TABLE ONLY public.addresses ALTER COLUMN id SET DEFAULT nextval('public.ad
421404
ALTER TABLE ONLY public.comments ALTER COLUMN id SET DEFAULT nextval('public.comments_id_seq'::regclass);
422405

423406

424-
--
425-
-- Name: notes id; Type: DEFAULT; Schema: public; Owner: -
426-
--
427-
428-
ALTER TABLE ONLY public.notes ALTER COLUMN id SET DEFAULT nextval('public.notes_id_seq'::regclass);
429-
430-
431407
--
432408
-- Name: migrations id; Type: DEFAULT; Schema: public; Owner: -
433409
--
@@ -508,13 +484,6 @@ ALTER TABLE ONLY public.comments
508484
ADD CONSTRAINT "PK_8bf68bc960f2b69e818bdb90dcb" PRIMARY KEY (id);
509485

510486

511-
--
512-
-- Name: notes PK_notes; Type: CONSTRAINT; Schema: public; Owner: -
513-
--
514-
515-
ALTER TABLE ONLY public.notes
516-
ADD CONSTRAINT "PK_notes" PRIMARY KEY (id);
517-
518487
--
519488
-- Name: migrations PK_8c82d7f526340ab734260ea46be; Type: CONSTRAINT; Schema: public; Owner: -
520489
--

libs/json-api-nestjs/src/lib/mock-utils/entities/notes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { Users, IUsers } from '.';
1212

1313
@Entity('notes')
1414
export class Notes {
15-
@PrimaryGeneratedColumn()
16-
public id: number;
15+
@PrimaryGeneratedColumn('uuid')
16+
public id: string;
1717

1818
@IsNotEmpty()
1919
@Column({

libs/json-api-nestjs/src/lib/mock-utils/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TypeOrmModule } from '@nestjs/typeorm';
22
import { DynamicModule } from '@nestjs/common';
3-
import { newDb } from 'pg-mem';
3+
import { DataType, newDb } from 'pg-mem';
44
import { readFileSync } from 'fs';
55
import { join } from 'path';
66

@@ -17,6 +17,8 @@ import {
1717
} from './entities';
1818
import { DataSource } from 'typeorm';
1919

20+
import { v4 } from 'uuid';
21+
2022
export * from './entities';
2123

2224
export const entities = [
@@ -48,6 +50,15 @@ export function mockDBTestModule(): DynamicModule {
4850
'PostgreSQL 12.5 on x86_64-pc-linux-musl, compiled by gcc (Alpine 10.2.1_pre1) 10.2.1 20201203, 64-bit',
4951
});
5052

53+
db.registerExtension('uuid-ossp', (schema) => {
54+
schema.registerFunction({
55+
name: 'uuid_generate_v4',
56+
returns: DataType.uuid,
57+
implementation: v4,
58+
impure: true,
59+
});
60+
});
61+
5162
db.public.none(dump);
5263
const backup = db.backup();
5364
return TypeOrmModule.forRootAsync({

0 commit comments

Comments
 (0)