Skip to content

Commit

Permalink
fix(schema): Fix nullable behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Dec 31, 2021
1 parent 2c40db3 commit 1b55161
Show file tree
Hide file tree
Showing 15 changed files with 401 additions and 89 deletions.
68 changes: 68 additions & 0 deletions docs/docs/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,74 @@ The @@Nullable@@ decorator is used allow a null value on a field while preservin
</Tab>
</Tabs>

## Any

The @@Any@@ decorator is used to allow any types:

<Tabs class="-code">
<Tab label="Model">

```typescript
import {Any} from "@tsed/schema";

export class Model {
@Any()
prop: any;
}
```

</Tab>
<Tab label="Json schema">

```json
{
"properties": {
"prop": {
"type": ["integer", "number", "string", "boolean", "array", "object", "null"]
}
},
"type": "object"
}
```

</Tab>
<Tab label="OS3">

```json
{
"properties": {
"prop": {
"nullable": true,
"oneOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "string"
},
{
"type": "boolean"
},
{
"type": "array"
},
{
"type": "object"
}
]
}
},
"type": "object"
}
```

</Tab>
</Tabs>


## Regular expressions

The @@Pattern@@ decorator is used to restrict a string to a particular regular expression. The regular expression syntax
Expand Down
5 changes: 5 additions & 0 deletions docs/docs/snippets/model/nullable-properties.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {Nullable, Required} from "@tsed/schema";
import {MyModel, MyModel2} from "./MyModel";

export class Model {
@Required(true, null) // allow null
@Nullable(String)
prop2: string | null;

// can be used with models (JsonSchema and OS3 only)
@Nullable(MyModel, MyModel2)
prop3: MyModel | MyModel2 | null;
}
23 changes: 4 additions & 19 deletions packages/platform/platform-express/test/array-body.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe("Array Body", () => {
});

describe("swagger", () => {
it("should return the swagger", async () => {
it("should return the swagger (OS3)", async () => {
const {body} = await request.get("/v3/docs/swagger.json").expect(200);

expect(body).to.deep.eq({
Expand Down Expand Up @@ -105,29 +105,24 @@ describe("Array Body", () => {
"application/json": {
"schema": {
"items": {
"nullable": true,
"oneOf": [
{
"nullable": true,
"type": "integer"
},
{
"nullable": true,
"type": "number"
},
{
"nullable": true,
"type": "string"
},
{
"nullable": true,
"type": "boolean"
},
{
"nullable": true,
"type": "array"
},
{
"nullable": true,
"type": "object"
}
]
Expand Down Expand Up @@ -157,29 +152,24 @@ describe("Array Body", () => {
"application/json": {
"schema": {
"items": {
"nullable": true,
"oneOf": [
{
"nullable": true,
"type": "integer"
},
{
"nullable": true,
"type": "number"
},
{
"nullable": true,
"type": "string"
},
{
"nullable": true,
"type": "boolean"
},
{
"nullable": true,
"type": "array"
},
{
"nullable": true,
"type": "object"
}
]
Expand Down Expand Up @@ -286,29 +276,24 @@ describe("Array Body", () => {
"content": {
"application/json": {
"schema": {
"nullable": true,
"oneOf": [
{
"nullable": true,
"type": "integer"
},
{
"nullable": true,
"type": "number"
},
{
"nullable": true,
"type": "string"
},
{
"nullable": true,
"type": "boolean"
},
{
"nullable": true,
"type": "array"
},
{
"nullable": true,
"type": "object"
}
]
Expand Down
6 changes: 3 additions & 3 deletions packages/platform/platform-params/src/decorators/useParam.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {isString, useDecorators} from "@tsed/core";
import {Any, Name} from "@tsed/schema";
import {Any} from "@tsed/schema";
import {ParamOptions} from "../domain/ParamOptions";
import {ParamTypes} from "../domain/ParamTypes";
import {ParamFn} from "./paramFn";
import {UseDeserialization} from "./useDeserialization";
import {UseParamExpression} from "./useParamExpression";
import {UseParamType} from "./useParamType";
import {UseType} from "./useType";
import {UseValidation} from "./useValidation";
Expand All @@ -19,7 +19,7 @@ function mapPipes(options: Partial<ParamOptions>) {
useType
? UseType(useType)
: ParamFn((entity, parameters) => {
if (entity.isCollection && entity.type === Object) {
if (entity.isCollection && entity.type === Object && paramType !== ParamTypes.FILES) {
Any()(...parameters);
}
}),
Expand Down
9 changes: 6 additions & 3 deletions packages/specs/schema/src/components/objectMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {JsonSchema} from "../domain/JsonSchema";
import {alterIgnore} from "../hooks/alterIgnore";
import {JsonSchemaOptions} from "../interfaces/JsonSchemaOptions";
import {execMapper, registerJsonSchemaMapper} from "../registries/JsonSchemaMapperContainer";
import {mapNullableType} from "../utils/mapNullableType";

/**
* Serialize Any object to a json schema
Expand All @@ -16,11 +17,13 @@ export function objectMapper(input: any, options: JsonSchemaOptions) {
return Object.entries(input).reduce<any>(
(obj, [key, value]: [string, any | JsonSchema]) => {
if (options.withIgnoredProps !== false && !alterIgnore(value, ctx)) {
// remove groups to avoid bad schema generation over children models
obj[key] = execMapper("item", value, {
const opts = {
...options,
groups: input?.$forwardGroups || value?.$forwardGroups ? options.groups : undefined
});
};
// remove groups to avoid bad schema generation over children models
obj[key] = execMapper("item", value, opts);
obj[key] = mapNullableType(obj[key], value, opts);
}

return obj;
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/components/schemaMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {SpecTypes} from "../domain/SpecTypes";
import {JsonSchemaOptions} from "../interfaces/JsonSchemaOptions";
import {execMapper, hasMapper, registerJsonSchemaMapper} from "../registries/JsonSchemaMapperContainer";
import {getRequiredProperties} from "../utils/getRequiredProperties";
import {mapNullableType} from "../utils/mapNullableType";
import {alterOneOf} from "../hooks/alterOneOf";
import {mapNullableType} from "../utils/mapNullableType";

/**
* @ignore
Expand Down

0 comments on commit 1b55161

Please sign in to comment.