Skip to content

Commit

Permalink
fix: add information about foreign key
Browse files Browse the repository at this point in the history
Signed-off-by: Muhammad Aaqil <aaqilcs102@gmail.com>
  • Loading branch information
aaqilniz committed Apr 12, 2024
1 parent 211d601 commit 3c4c5b8
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 9 deletions.
41 changes: 41 additions & 0 deletions packages/openapi-v3/src/__tests__/unit/json-to-schema.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,47 @@ describe('jsonToSchemaObject', () => {
propertyConversionTest(allOfDef, expectedAllOf);
});

it('convert description with relation to x-relationships', () => {
const descriptionWithRelation: JsonSchema = {
description:
'(tsType: ProductWithRelations, ' +
'schemaOptions: { includeRelations: true }), ' +
'{"relationships":{"products":{"description":"Category have many Product.","type":"array","items":{"$ref":"#/definitions/ProductWithRelations"},"foreignKeys":{"categoryId":"Category"}}}}',
};
type Relationship = {
description?: string;
type?: string;
$ref?: string;
items?: {$ref: string};
// eslint-disable-next-line @typescript-eslint/naming-convention
'x-foreign-keys'?: {[x: string]: string};
};
const expectedDescriptionWithRelation: SchemaObject & {
// eslint-disable-next-line @typescript-eslint/naming-convention
'x-relationships': {[x: string]: Relationship};
} = {
description:
'(tsType: ProductWithRelations, schemaOptions: { includeRelations: true }), ',
'x-relationships': {
products: {
description: 'Category have many Product.',
items: {
$ref: '#/definitions/ProductWithRelations',
},
type: 'array',
'x-foreign-keys': {
categoryId: 'Category',
},
},
},
'x-typescript-type': 'ProductWithRelations',
};
propertyConversionTest(
descriptionWithRelation,
expectedDescriptionWithRelation,
);
});

it('converts anyOf', () => {
const anyOfDef: JsonSchema = {
anyOf: [typeDef, typeDef],
Expand Down
17 changes: 17 additions & 0 deletions packages/openapi-v3/src/json-to-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@ export function jsonToSchemaObject(
delete result[converted];
// Check if the description contains information about TypeScript type
const matched = result.description?.match(/^\(tsType: (.+), schemaOptions:/);
if (result.description) {
const relationMatched = result.description.match(/\{"relationships".*$/s);
if (relationMatched) {
const stringifiedRelation = relationMatched[0].replace(
/foreignKey/g,
'x-foreign-key',
);
if (stringifiedRelation) {
result['x-relationships'] =
JSON.parse(stringifiedRelation)['relationships'];
result.description = result.description.replace(
/\{"relationships".*$/s,
'',
);
}
}
}
if (matched) {
result['x-typescript-type'] = matched[1];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1126,14 +1126,14 @@ describe('build-schema', () => {
type: 'object',
description:
`(tsType: ProductWithRelations, ` +
`schemaOptions: { includeRelations: true })`,
`schemaOptions: { includeRelations: true }), ` +
`{\"relationships\":{\"category\":{\"description\":\"Product belongs to Category.\",\"type\":\"object\",\"$ref\":\"#/definitions/CategoryWithRelations\",\"foreignKeys\":{\"categoryId\":\"Category\"}}}}`,
properties: {
id: {type: 'number'},
categoryId: {type: 'number'},
category: {
$ref: '#/definitions/CategoryWithRelations',
},
foreignKey: 'categoryId' as JsonSchema,
},
additionalProperties: false,
},
Expand All @@ -1152,7 +1152,8 @@ describe('build-schema', () => {
type: 'object',
description:
`(tsType: CategoryWithRelations, ` +
`schemaOptions: { includeRelations: true })`,
`schemaOptions: { includeRelations: true }), ` +
`{\"relationships\":{\"products\":{\"description\":\"Category have many Product.\",\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/ProductWithRelations\"},\"foreignKeys\":{\"categoryId\":\"Category\"}}}}`,
};
const jsonSchema = getJsonSchema(Category, {includeRelations: true});
expect(jsonSchema).to.deepEqual(expectedSchema);
Expand Down Expand Up @@ -1180,14 +1181,14 @@ describe('build-schema', () => {
type: 'object',
description:
`(tsType: ProductWithRelations, ` +
`schemaOptions: { includeRelations: true })`,
`schemaOptions: { includeRelations: true }), ` +
`{\"relationships\":{\"category\":{\"description\":\"Product belongs to CategoryWithoutProp.\",\"type\":\"object\",\"$ref\":\"#/definitions/CategoryWithoutPropWithRelations\",\"foreignKeys\":{\"categoryId\":\"CategoryWithoutProp\"}}}}`,
properties: {
id: {type: 'number'},
categoryId: {type: 'number'},
category: {
$ref: '#/definitions/CategoryWithoutPropWithRelations',
},
foreignKey: 'categoryId' as JsonSchema,
},
additionalProperties: false,
},
Expand All @@ -1203,7 +1204,8 @@ describe('build-schema', () => {
type: 'object',
description:
`(tsType: CategoryWithoutPropWithRelations, ` +
`schemaOptions: { includeRelations: true })`,
`schemaOptions: { includeRelations: true }), ` +
`{\"relationships\":{\"products\":{\"description\":\"CategoryWithoutProp have many Product.\",\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/ProductWithRelations\"},\"foreignKeys\":{\"categorywithoutpropId\":\"CategoryWithoutProp\"}}}}`,
};

// To check for case when there are no other properties than relational
Expand Down
129 changes: 126 additions & 3 deletions packages/repository-json-schema/src/build-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,11 +577,134 @@ export function modelToJsonSchema<T extends object>(

result.properties[relMeta.name] =
result.properties[relMeta.name] || propDef;
if ((relMeta as {keyFrom: string}).keyFrom) {
result.properties.foreignKey = (relMeta as {keyFrom: string})
.keyFrom as JsonSchema;
type Relationship = {
description?: string;
type?: string;
$ref?: string;
items?: {$ref: string};
foreignKeys?: {[x: string]: string};
};

const foreignKey: {[x: string]: string} = {};
let relationships: {[x: string]: Relationship} = {};

relationships = {};
if (!relationships[relMeta.name]) {
relationships[relMeta.name] = {};
}

if (relMeta.type === 'belongsTo') {
let keyFrom = (relMeta as {keyFrom: string}).keyFrom;
if (!keyFrom) {
keyFrom = targetType.name.toLowerCase() + 'Id';
}
foreignKey[keyFrom] = targetType.name;
relationships[relMeta.name].description =
`${relMeta.source.name} belongs to ${targetType.name}.`;
relationships[relMeta.name].type = 'object';
relationships[relMeta.name].$ref =
`#/definitions/${targetSchema.title}`;
}
if (relMeta.type === 'hasMany') {
if ((relMeta as {through: object}).through) {
relationships = {};
if (!relationships[relMeta.name]) {
relationships[relMeta.name] = {};
}
let keyTo = (
(relMeta as {through: object}).through as {keyTo: string}
).keyTo;
let keyFrom = (
(relMeta as {through: object}).through as {keyFrom: string}
).keyFrom;

if (!keyTo) {
keyTo = targetType.name.toLowerCase() + 'Id';
}
if (!keyFrom) {
keyFrom = relMeta.source.name.toLowerCase() + 'Id';
}

foreignKey[keyTo] = targetType.name;
foreignKey[keyFrom] = relMeta.source.name;

relationships[relMeta.name].description =
`${relMeta.source.name} have many ${targetType.name}.`;
relationships[relMeta.name].type = 'object';
relationships[relMeta.name].$ref =
`#/definitions/${targetSchema.title}`;
} else {
let keyFrom = (relMeta as {keyFrom: string}).keyFrom;
if (!keyFrom) {
keyFrom = relMeta.source.name.toLowerCase() + 'Id';
}
foreignKey[keyFrom] = relMeta.source.name;
relationships[relMeta.name].description =
`${relMeta.source.name} have many ${targetType.name}.`;
relationships[relMeta.name].type = 'array';
relationships[relMeta.name].items = {
$ref: `#/definitions/${targetSchema.title}`,
};
}
}

if (relMeta.type === 'hasOne') {
relationships = {};
if (!relationships[relMeta.name]) {
relationships[relMeta.name] = {};
}

let keyTo = (relMeta as {keyTo: string}).keyTo;

if (!keyTo) {
keyTo = relMeta.source.name.toLowerCase() + 'Id';
}
foreignKey[keyTo] = relMeta.source.name;
relationships[relMeta.name].description =
`${relMeta.source.name} have one ${targetType.name}.`;
relationships[relMeta.name].type = 'object';
relationships[relMeta.name].$ref =
`#/definitions/${targetSchema.title}`;
}

if (relMeta.type === 'referencesMany') {
let keyFrom = (relMeta as {keyFrom: string}).keyFrom;
if (!keyFrom) {
keyFrom = targetType.name.toLowerCase() + 'Ids';
}
foreignKey[keyFrom] = targetType.name;
relationships[relMeta.name].description =
`${relMeta.source.name} references many ${targetType.name}.`;
relationships[relMeta.name].type = 'array';
relationships[relMeta.name].items = {
$ref: `#/definitions/${targetSchema.title}`,
};
}
relationships[relMeta.name].foreignKeys = foreignKey;
if (result.description) {
if (result.description.includes('relationships')) {
const relationMatched =
result.description.match(/\{"relationships".*$/s);
if (relationMatched) {
const {relationships: existingRelation} = JSON.parse(
relationMatched[0],
);
existingRelation[Object.keys(relationships)[0]] = {
...relationships,
};
result.description = result.description.replace(
/\{"relationships".*$/s,
'',
);
result.description =
result.description +
`, ${JSON.stringify({relationships: existingRelation})}`;
}
} else {
result.description =
result.description + `, ${JSON.stringify({relationships})}`;
}
}
includeReferencedSchema(targetSchema.title!, targetSchema);
}
}
Expand Down

0 comments on commit 3c4c5b8

Please sign in to comment.