Skip to content

Commit 5279c58

Browse files
authored
fix: accessing nested object properties by reference (#88)
1 parent 8da5388 commit 5279c58

File tree

7 files changed

+119
-8
lines changed

7 files changed

+119
-8
lines changed

scripts/testCodeGen.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Writer from "./writer";
33
const main = () => {
44
Writer.generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true);
55
Writer.generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false);
6+
Writer.generateTypedefCodeOnly("test/json.properties/index.yml", "test/code/typedef-only/json.properties.ts", false);
67

78
Writer.generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false });
89
Writer.generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true });

src/internal/OpenApiTools/TypeNodeContext.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as Path from "path";
2+
import DotProp from "dot-prop";
23

34
import ts from "typescript";
45

@@ -8,6 +9,8 @@ import * as TypeScriptCodeGenerator from "../TsGenerator";
89
import * as ConverterContext from "./ConverterContext";
910
import * as ToTypeNode from "./toTypeNode";
1011
import type * as Walker from "./Walker";
12+
import * as Guard from "./Guard";
13+
import * as Reference from "./components/Reference";
1114

1215
export interface ReferencePathSet {
1316
pathArray: string[];
@@ -93,6 +96,27 @@ export const create = (
9396
const { pathArray, base } = generatePath(entryPoint, currentPoint, referencePath);
9497
return calculateReferencePath(store, base, pathArray, converterContext);
9598
};
99+
const findSchemaByPathArray = (
100+
pathArray: string[],
101+
remainPathArray: string[] = [],
102+
): OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition => {
103+
const schema = DotProp.get<OpenApi.Schema>(rootSchema, pathArray.join("."));
104+
if (!schema) {
105+
return findSchemaByPathArray(pathArray.slice(0, pathArray.length - 1), [pathArray[pathArray.length - 1], ...remainPathArray]);
106+
}
107+
if (Guard.isReference(schema)) {
108+
const ref = Reference.generate(entryPoint, entryPoint, schema);
109+
return findSchemaByPathArray(ref.path.split("/"), remainPathArray);
110+
}
111+
if (remainPathArray.length) {
112+
const moreNestSchema = DotProp.get<OpenApi.Schema>(schema, remainPathArray.join("."));
113+
if (!moreNestSchema) {
114+
throw new Error("Not found");
115+
}
116+
return moreNestSchema;
117+
}
118+
return schema;
119+
};
96120
const setReferenceHandler: ToTypeNode.Context["setReferenceHandler"] = (currentPoint, reference) => {
97121
if (store.hasStatement(reference.path, ["interface", "typeAlias"])) {
98122
return;
@@ -107,6 +131,7 @@ export const create = (
107131
rootSchema,
108132
setReferenceHandler,
109133
resolveReferencePath,
134+
findSchemaByPathArray,
110135
},
111136
converterContext,
112137
);
@@ -133,6 +158,7 @@ export const create = (
133158
rootSchema,
134159
setReferenceHandler,
135160
resolveReferencePath,
161+
findSchemaByPathArray,
136162
},
137163
converterContext,
138164
),
@@ -161,5 +187,10 @@ export const create = (
161187
}
162188
}
163189
};
164-
return { rootSchema, setReferenceHandler: setReferenceHandler, resolveReferencePath: resolveReferencePath };
190+
return {
191+
rootSchema,
192+
setReferenceHandler: setReferenceHandler,
193+
resolveReferencePath: resolveReferencePath,
194+
findSchemaByPathArray: findSchemaByPathArray,
195+
};
165196
};

src/internal/OpenApiTools/components/Schemas.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import DotProp from "dot-prop";
2-
31
import type { OpenApi } from "../../../types";
42
import * as Logger from "../..//Logger";
53
import { UnSupportError } from "../../Exception";
@@ -39,7 +37,7 @@ export const generateNamespace = (
3937
name: convertContext.escapeReferenceDeclarationText(maybeResolvedName),
4038
});
4139
}
42-
const schema = DotProp.get(context.rootSchema, pathArray.join(".")) as any;
40+
const schema = context.findSchemaByPathArray(pathArray);
4341
return ToTypeNode.convert(entryPoint, currentPoint, factory, schema, context, convertContext, { parent: schema });
4442
};
4543
return store.addStatement(`${basePath}/${name}`, {
@@ -82,7 +80,7 @@ export const generateNamespace = (
8280
const schema = InferredType.getInferredType(targetSchema);
8381
const path = `${basePath}/${name}`;
8482
if (!schema) {
85-
// Schemaが特定できないためWarningを出力する
83+
// Outputs Warning because Schema cannot be identified
8684
Logger.warn(`Warning: Schema could not be identified. Therefore, it is treated as any. ${name}`);
8785
return store.addStatement(
8886
path,

src/internal/OpenApiTools/toTypeNode.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import DotProp from "dot-prop";
21
import ts from "typescript";
32

43
import type { OpenApi } from "../../types";
@@ -19,14 +18,17 @@ export interface ResolveReferencePath {
1918
* @example components.a.b.c.dの場合 ["a", "b", "c", "d"].length = 4
2019
**/
2120
depth: number;
22-
/** 入力$refを分解したモノ(#は除く) */
21+
/**
22+
* Input $ref divided by / (except #)
23+
*/
2324
pathArray: string[];
2425
}
2526

2627
export interface Context {
2728
readonly rootSchema: OpenApi.Document;
2829
setReferenceHandler: (currentPoint: string, reference: Reference.Type<OpenApi.Schema | OpenApi.JSONSchemaDefinition>) => void;
2930
resolveReferencePath: (currentPoint: string, referencePath: string) => ResolveReferencePath;
31+
findSchemaByPathArray: (paths: string[]) => OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition;
3032
}
3133

3234
export type Convert = (
@@ -111,7 +113,7 @@ export const convert: Convert = (
111113
if (depth === 2) {
112114
return factory.TypeReferenceNode.create({ name: converterContext.escapeReferenceDeclarationText(maybeResolvedName) });
113115
} else {
114-
const resolveSchema = DotProp.get(context.rootSchema, pathArray.join(".")) as any;
116+
const resolveSchema = context.findSchemaByPathArray(pathArray);
115117
return convert(entryPoint, currentPoint, factory, resolveSchema, context, converterContext, { parent: schema });
116118
}
117119
}

test/__tests__/__snapshots__/typedef-only-test.ts.snap

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Typedef only Reference test that include nested properties 1`] = `
4+
"//
5+
// Generated by @himenon/openapi-typescript-code-generator
6+
//
7+
// OpenApi : 3.0.0
8+
//
9+
// License : MIT
10+
//
11+
12+
13+
export namespace Schemas {
14+
export interface Foo {
15+
bar?: {
16+
baz?: string;
17+
bazbaz?: {
18+
hoge?: number;
19+
};
20+
};
21+
}
22+
export type Bar = {
23+
baz?: string;
24+
bazbaz?: {
25+
hoge?: number;
26+
};
27+
};
28+
export type Baz = string;
29+
export type Hoge = number;
30+
export type BazBaz = {
31+
hoge?: number;
32+
};
33+
}
34+
"
35+
`;
36+
337
exports[`Typedef only typedef-api.test.domain 1`] = `
438
"//
539
// Generated by @himenon/openapi-typescript-code-generator

test/__tests__/typedef-only-test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,9 @@ describe("Typedef only", () => {
1414
const text = Utils.replaceVersionInfo(generateCode);
1515
expect(text).toMatchSnapshot();
1616
});
17+
test("Reference test that include nested properties", () => {
18+
const generateCode = fs.readFileSync("test/code/typedef-only/json.properties.ts", { encoding: "utf-8" });
19+
const text = Utils.replaceVersionInfo(generateCode);
20+
expect(text).toMatchSnapshot();
21+
});
1722
});

test/json.properties/index.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
openapi: 3.0.0
2+
info:
3+
version: 1.0.0
4+
title: api.test.domain
5+
description: Library test schema
6+
license:
7+
name: MIT
8+
9+
servers:
10+
- url: "http://dev.api.test.domain/"
11+
description: Development Environment
12+
- url: "https://api.test.domain/"
13+
description: Production Environment
14+
15+
tags:
16+
- name: test
17+
18+
components:
19+
schemas:
20+
Foo:
21+
type: object
22+
properties:
23+
bar:
24+
type: object
25+
properties:
26+
baz:
27+
type: string
28+
bazbaz:
29+
type: object
30+
properties:
31+
hoge:
32+
type: number
33+
Bar:
34+
$ref: "#/components/schemas/Foo/properties/bar"
35+
Baz:
36+
$ref: "#/components/schemas/Bar/properties/baz"
37+
Hoge:
38+
$ref: "#/components/schemas/Bar/properties/bazbaz/properties/hoge"
39+
BazBaz:
40+
$ref: "#/components/schemas/Bar/properties/bazbaz"

0 commit comments

Comments
 (0)