Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose metaData return prop #65

Merged
merged 1 commit into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/little-rats-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openapi-ts-json-schema': minor
---

Expose `metaData` return property holding generated schemas meta data
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,39 @@ Circular `$ref`s can be technically resolved with `experimentalImportRefs` optio

Take a look at the [Developer's notes](./docs/developer-notes.md) for a few more in-depth explanations.

## Return values

Beside generating the expected schema files under `outputPath`, `openapiToTsJsonSchema` returns the following data:

```ts
{
// The path where the schemas are generated
outputPath: string;
metaData: {
// Meta data of the generated schemas
schemas: Map<
string,
{
// Valid filename for given schema (without extension).
schemaFileName: string;
// Absolute path pointing to schema folder
schemaAbsoluteDirName: string;
// Absolute path pointing to schema file
schemaAbsolutePath: string;
// Absolute import path (without extension)
schemaAbsoluteImportPath: string;
// Unique JavaScript identifier used as import name
schemaUniqueName: string;
// The actual JSON schema
schema: JSONSchema;
// True is schemas is used as a `$ref`
isRef: boolean;
}
>;
}
}
```

## Todo

- Consider merging "operation" and "path" parameters definition
Expand Down
21 changes: 11 additions & 10 deletions src/openapiToTsJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
SchemaPatcher,
convertOpenApiToJsonSchema,
convertOpenApiParameters,
SchemaMetaInfoMap,
SchemaMetaDataMap,
JSONSchema,
addSchemaMetaInfo,
addSchemaToMetaData,
pathToRef,
} from './utils';

Expand All @@ -31,7 +31,7 @@ export async function openapiToTsJsonSchema({
outputPath?: string;
silent?: boolean;
experimentalImportRefs?: boolean;
}) {
}): Promise<{ outputPath: string; metaData: { schemas: SchemaMetaDataMap } }> {
if (definitionPathsToGenerateFrom.length === 0 && !silent) {
console.log(
`[openapi-ts-json-schema] ⚠️ No schemas will be generated since definitionPathsToGenerateFrom option is empty`,
Expand Down Expand Up @@ -97,14 +97,14 @@ export async function openapiToTsJsonSchema({
);

const jsonSchema = convertOpenApiParameters(dereferencedJsonSchema);
const schemas: SchemaMetaInfoMap = new Map();
const schemaMetaDataMap: SchemaMetaDataMap = new Map();

// Generate schema meta info for inlined refs, first
if (experimentalImportRefs) {
for (const [ref, schema] of inlinedRefs) {
addSchemaMetaInfo({
addSchemaToMetaData({
id: ref,
schemas,
schemaMetaDataMap,
schema,
outputPath,
schemaPatcher,
Expand All @@ -124,9 +124,9 @@ export async function openapiToTsJsonSchema({
schemaName,
});

addSchemaMetaInfo({
addSchemaToMetaData({
id,
schemas,
schemaMetaDataMap,
schema: definitionSchemas[schemaName],
outputPath,
schemaPatcher,
Expand All @@ -137,13 +137,14 @@ export async function openapiToTsJsonSchema({
}

await makeJsonSchemaFiles({
schemas,
schemaMetaDataMap,
});

if (!silent) {
console.log(
`[openapi-ts-json-schema] ✅ JSON schema models generated at ${outputPath}`,
);
}
return { outputPath };

return { outputPath, metaData: { schemas: schemaMetaDataMap } };
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import path from 'path';
import namify from 'namify';
import filenamify from 'filenamify';
import {
SchemaMetaInfoMap,
SchemaMetaInfo,
SchemaMetaDataMap,
SchemaMetaData,
JSONSchema,
replaceInlinedRefsWithStringPlaceholder,
patchJsonSchema,
Expand All @@ -13,11 +13,11 @@ import {
} from '.';

/*
* Just an utility function to add entries to SchemaMetaInfoMap Map keyed by ref
* Just an utility function to add entries to SchemaMetaDataMap Map keyed by ref
*/
export function addSchemaMetaInfo({
export function addSchemaToMetaData({
id,
schemas,
schemaMetaDataMap,
schema,
isRef,
// Options
Expand All @@ -26,15 +26,15 @@ export function addSchemaMetaInfo({
experimentalImportRefs,
}: {
id: string;
schemas: SchemaMetaInfoMap;
schemaMetaDataMap: SchemaMetaDataMap;
schema: JSONSchema;
isRef: boolean;
outputPath: string;
schemaPatcher?: SchemaPatcher;
experimentalImportRefs: boolean;
}): void {
// Do not override existing meta info of inlined schemas
if (schemas.has(id) === false) {
if (schemaMetaDataMap.has(id) === false) {
const { schemaRelativeDirName, schemaName } = refToPath(id);
const schemaRelativePath = path.join(schemaRelativeDirName, schemaName);
const originalSchema = experimentalImportRefs
Expand All @@ -49,7 +49,7 @@ export function addSchemaMetaInfo({
schemaFileName,
);

const metaInfo: SchemaMetaInfo = {
const metaInfo: SchemaMetaData = {
schemaFileName,
schemaAbsoluteDirName,
schemaAbsoluteImportPath,
Expand All @@ -58,6 +58,6 @@ export function addSchemaMetaInfo({
schema: patchedSchema,
isRef,
};
schemas.set(id, metaInfo);
schemaMetaDataMap.set(id, metaInfo);
}
}
6 changes: 3 additions & 3 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export type {
JSONSchema,
OpenApiSchema,
SchemaPatcher,
SchemaMetaInfo,
SchemaMetaInfoMap,
SchemaMetaData,
SchemaMetaDataMap,
} from './types';
export { addSchemaMetaInfo } from './addSchemaMetaInfo';
export { addSchemaToMetaData } from './addSchemaToMetaData';
export { makeRelativePath } from './makeRelativePath';
14 changes: 7 additions & 7 deletions src/utils/jsonSchemaToTsConst/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import prettier from 'prettier';
import { stringify } from 'comment-json';
import { replacePlaceholdersWithImportedSchemas } from './replacePlaceholdersWithImportedSchemas';
import type { SchemaMetaInfoMap, SchemaMetaInfo } from '../';
import type { SchemaMetaDataMap, SchemaMetaData } from '../';

export async function jsonSchemaToTsConst({
schemaMetaInfo,
schemas,
metaData,
schemaMetaDataMap,
}: {
schemaMetaInfo: SchemaMetaInfo;
schemas: SchemaMetaInfoMap;
metaData: SchemaMetaData;
schemaMetaDataMap: SchemaMetaDataMap;
}): Promise<string> {
const { schema, schemaAbsoluteDirName } = schemaMetaInfo;
const { schema, schemaAbsoluteDirName } = metaData;

// Stringify schema with "node-comment-json" to generate inline comments
const stringifiedSchema = stringify(schema, null, 2);
Expand All @@ -20,7 +20,7 @@ export async function jsonSchemaToTsConst({
tsSchema = replacePlaceholdersWithImportedSchemas({
schemaAsText: tsSchema,
schemaAbsoluteDirName,
schemas,
schemaMetaDataMap,
});

const formattedSchema = await prettier.format(tsSchema, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { SchemaMetaInfoMap, makeRelativePath, PLACEHOLDER_REGEX } from '..';
import { SchemaMetaDataMap, makeRelativePath, PLACEHOLDER_REGEX } from '..';

/**
* Replace Refs placeholders with imported schemas
*/
export function replacePlaceholdersWithImportedSchemas({
schemaAsText,
schemaAbsoluteDirName,
schemas,
schemaMetaDataMap,
}: {
schemaAsText: string;
schemaAbsoluteDirName: string;
schemas: SchemaMetaInfoMap;
schemaMetaDataMap: SchemaMetaDataMap;
}): string {
const importStatements = new Set<string>();

// Replace placeholder occurrences with the relevant imported schema name
let schema = schemaAsText.replaceAll(PLACEHOLDER_REGEX, (_match, ref) => {
const importedSchema = schemas.get(ref);
const importedSchema = schemaMetaDataMap.get(ref);

/* istanbul ignore if: It should not be possible to hit this condition -- @preserve */
if (!importedSchema) {
throw new Error(
'[openapi-ts-json-schema] No matching schema found in "schemas"',
'[openapi-ts-json-schema] No matching schema found in "schemaMetaDataMap"',
);
}

Expand Down
14 changes: 7 additions & 7 deletions src/utils/makeJsonSchemaFiles.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import fs from 'fs/promises';
import { jsonSchemaToTsConst, SchemaMetaInfoMap } from '.';
import { jsonSchemaToTsConst, SchemaMetaDataMap } from '.';

/**
* Save TS JSON schema with the expected naming conventions
*/
export async function makeJsonSchemaFiles({
schemas,
schemaMetaDataMap,
}: {
schemas: SchemaMetaInfoMap;
schemaMetaDataMap: SchemaMetaDataMap;
}) {
for (const [_, schemaMetaInfo] of schemas) {
for (const [_, metaData] of schemaMetaDataMap) {
const tsSchema = await jsonSchemaToTsConst({
schemaMetaInfo,
schemas,
metaData,
schemaMetaDataMap,
});

const { schemaAbsoluteDirName, schemaAbsolutePath } = schemaMetaInfo;
const { schemaAbsoluteDirName, schemaAbsolutePath } = metaData;
await fs.mkdir(schemaAbsoluteDirName, { recursive: true });
await fs.writeFile(schemaAbsolutePath, tsSchema);
}
Expand Down
6 changes: 3 additions & 3 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type SchemaPatcher = (params: { schema: JSONSchema }) => void;
* @prop `schema` - The actual JSON schema
* @prop `isRef` - Mark schemas used as `$ref`
*/
export type SchemaMetaInfo = {
export type SchemaMetaData = {
schemaFileName: string;
schemaAbsoluteDirName: string;
schemaAbsolutePath: string;
Expand All @@ -21,7 +21,7 @@ export type SchemaMetaInfo = {
schema: JSONSchema;
isRef: boolean;
};
export type SchemaMetaInfoMap = Map<
export type SchemaMetaDataMap = Map<
string, // Schema file relative path
SchemaMetaInfo
SchemaMetaData
>;
31 changes: 31 additions & 0 deletions test/metaData.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from 'path';
import fs from 'fs';
import { describe, it, expect } from 'vitest';
import { openapiToTsJsonSchema } from '../src';

const fixtures = path.resolve(__dirname, 'fixtures');

describe('Returned "metaData"', async () => {
it('returns expected data', async () => {
const { metaData } = await openapiToTsJsonSchema({
openApiSchema: path.resolve(fixtures, 'mini-referenced/specs.yaml'),
definitionPathsToGenerateFrom: ['components.months'],
experimentalImportRefs: true,
silent: false,
});

const answerMeta = metaData.schemas.get('#/components/schemas/Answer');
const januaryMeta = metaData.schemas.get('#/components/months/January');

expect(answerMeta).toBeDefined();
expect(januaryMeta).toBeDefined();
expect(metaData.schemas.size).toBe(2);

if (!answerMeta || !januaryMeta) {
throw 'Unexpected undefined meta data';
}

expect(fs.existsSync(answerMeta.schemaAbsolutePath)).toBe(true);
expect(fs.existsSync(januaryMeta.schemaAbsolutePath)).toBe(true);
});
});