Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
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
4 changes: 3 additions & 1 deletion packages/openapi-generator/src/file-serializer/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export function serializeSchema(schema: OpenApiSchema): string {
const type = serializeSchema(schema.items);
return schema.uniqueItems
? `Set<${type}>`
: 'properties' in schema.items
: ['properties', 'allOf', 'oneOf', 'anyOf'].some(
prop => prop in schema.items
)
? `(${type})[]`
: `${type}[]`;
}
Expand Down
104 changes: 104 additions & 0 deletions packages/openapi-generator/src/parser/schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,110 @@ describe('parseSchema', () => {
);
});

it('parses xOf schema ignoring type:object at same level', async () => {
const schema: OpenAPIV3.SchemaObject = {
type: 'object',
oneOf: [
{ type: 'object' },
{
anyOf: [
{ type: 'object' },
{ allOf: [{ type: 'object' }, { type: 'string' }] }
]
}
]
};
expect(parseSchema(schema, await createTestRefs(), defaultOptions)).toEqual(
{
oneOf: [
emptyObjectSchema,
{
anyOf: [
emptyObjectSchema,
{ allOf: [emptyObjectSchema, { type: 'string' }] }
]
}
]
}
);
});

it('normalizes xOf schemas with properties at same level', async () => {
const schema: OpenAPIV3.SchemaObject = {
type: 'object',
properties: { prop1: { type: 'string' } },
oneOf: [
{ type: 'object' },
{
allOf: [{ type: 'object' }, { type: 'string' }]
}
]
};
expect(parseSchema(schema, await createTestRefs(), defaultOptions)).toEqual(
{
oneOf: [
emptyObjectSchema,
{ allOf: [emptyObjectSchema, { type: 'string' }] },
{
additionalProperties: { type: 'any' },
properties: [
{
name: 'prop1',
description: undefined,
required: false,
schema: {
type: 'string'
},
schemaProperties: {}
}
]
}
]
}
);
});

it('parses xOf schemas with required', async () => {
const schema: OpenAPIV3.SchemaObject = {
oneOf: [
{ type: 'object' },
{
allOf: [
{ type: 'object', properties: { prop1: { type: 'string' } } },
{ type: 'string' }
],
required: ['prop1']
}
]
};
expect(parseSchema(schema, await createTestRefs(), defaultOptions)).toEqual(
{
oneOf: [
emptyObjectSchema,
{
allOf: [
{
additionalProperties: { type: 'any' },
properties: [
{
name: 'prop1',
description: undefined,
required: true,
schema: {
type: 'string'
},
schemaProperties: {}
}
]
},
{ type: 'string' }
]
}
]
}
);
});

it('parses not schema', async () => {
const schema: OpenAPIV3.SchemaObject = {
not: { type: 'object' }
Expand Down
46 changes: 37 additions & 9 deletions packages/openapi-generator/src/parser/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@ export function parseSchema(
return parseArraySchema(schema, refs, options);
}

if (
schema.type === 'object' ||
schema.properties ||
'additionalProperties' in schema
) {
return parseObjectSchema(schema, refs, options);
}

if (schema.enum?.length) {
return parseEnumSchema(schema, options);
}
Expand All @@ -66,6 +58,16 @@ export function parseSchema(
return parseXOfSchema(schema, refs, 'anyOf', options);
}

// An object schema should be parsed after allOf, anyOf, oneOf.
// When object.properties are at the same level with anyOf, oneOf, allOf, they should be treated as part of allOf, etc.
if (
schema.type === 'object' ||
schema.properties ||
schema.additionalProperties
) {
return parseObjectSchema(schema, refs, options);
}

if (schema.not) {
return {
not: parseSchema(schema.not, refs, options)
Expand Down Expand Up @@ -221,8 +223,34 @@ function parseXOfSchema(
xOf: 'oneOf' | 'allOf' | 'anyOf',
options: ParserOptions
): any {
let normalizedSchema: OpenAPIV3.NonArraySchemaObject = schema;
if (
schema.properties ||
(schema.additionalProperties &&
typeof schema.additionalProperties === 'object')
) {
logger.info(
`Detected schema with ${xOf} and properties in the same level. This was refactored to a schema with ${xOf} only, containing all the properties from the top level.`
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { [xOf]: _, ...objectSchema } = schema;

normalizedSchema = { [xOf]: [...(schema[xOf] || []), objectSchema] };
}
return {
[xOf]: (schema[xOf] || []).map(entry => parseSchema(entry, refs, options))
[xOf]: (normalizedSchema[xOf] || []).map(entry =>
parseSchema(
{
...entry,
required: [
...('required' in entry && entry.required ? entry.required : []),
...(normalizedSchema.required || [])
]
},
refs,
options
)
)
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
export * from './test-entity';
export * from './test-entity-2';
export * from './test-entity-3';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
export * from './test-entity';
export * from './test-entity-2';
export * from './test-entity-3';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved.
*
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
import type { TestEntity } from './test-entity';
/**
* Representation of the 'TestEntity2' schema.
*/
export type TestEntity2 = TestEntity & {
booleanProperty: boolean;
integerProperty2?: number;
} & Record<string, any>;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved.
*
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
import type { TestEntity } from './test-entity';
/**
* Representation of the 'TestEntity2' schema.
*/
export type TestEntity2 = TestEntity & {
booleanProperty: boolean;
integerProperty2?: number;
} & Record<string, any>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved.
*
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
import type { TestEntity } from './test-entity';
/**
* Representation of the 'TestEntity3' schema.
*/
export type TestEntity3 =
| TestEntity
| ({
booleanProperty?: boolean;
} & Record<string, any>);

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved.
*
* This is a generated file powered by the SAP Cloud SDK for JavaScript.
*/
import type { TestEntity } from './test-entity';
/**
* Representation of the 'TestEntity3' schema.
*/
export type TestEntity3 =
| TestEntity
| ({
booleanProperty?: boolean;
} & Record<string, any>);
20 changes: 20 additions & 0 deletions test-resources/openapi-service-specs/swagger-yaml-service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,23 @@ definitions:
type: integer
description: An integer property
required: false
TestEntity_2:
type: object
allOf:
- $ref: '#/definitions/TestEntity'
- properties:
booleanProperty:
type: boolean
required: false
integerProperty2:
type: integer
required:
- booleanProperty
TestEntity_3:
type: object
anyOf:
- $ref: '#/definitions/TestEntity'
properties:
booleanProperty:
type: boolean

Loading