Skip to content
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
21 changes: 21 additions & 0 deletions docs/site/Model.md
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,27 @@ class TestModel {
}
```

To define a nested array property, you must provide the `jsonSchema` field to
describe the sub-array property. For example:

```ts
@model()
class TestModel {
// alternatively use @property.array('array')
@property.array(Array, {
jsonSchema: {
type: 'array',
items: {type: 'string'},
Comment on lines +862 to +864
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have another question of the design. Do we need to specify these two fields? if so, why do we need type: 'array' as we already use the decorator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agnes512 the jsonSchema here is for the sub array property,
e.g. For Array<Array<string>>, the json schema describes Array<string>

If the current doc confuses you:

To define a nested array property, you must provide the jsonSchema field to describe the sub-array property.

Any suggestion on how to make it more clear?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, then I am good with it 👍

},
})
nestedArr: Array<Array<string>>;
}
```

If the `jsonSchema` field is missing, you will get an error saying

> You must provide the "jsonSchema" field when define a nested array property'

### Validation Rules

You can also specify the validation rules in the field `jsonSchema`. For
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,34 @@ describe('build-schema', () => {
expectValidJsonSchema(jsonSchema);
});

it('properly converts nested array property when json schema provided', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any tests on querying/filtering the nested arrays?

Copy link
Contributor Author

@jannyHou jannyHou May 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agnes512 @loopback/repository-json-schema doesn't have it. If we add tests, need to do them in the acceptance tests for connectors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned it is because when I was triaging loopbackio/loopback-connector-postgresql#441, I realized that not all connectors support arrays/nested arrays. It'd be good if we can have them documented and tested. Just some thoughts 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that not all connectors support arrays/nested arrays.

Yeah that's the problem, the original issue #4754 is not trying to support the full feature, it's a fix for the misuse of decorator.

It'd be good if we can have them documented and tested.

It's not a trivial thing to test across connectors, let's do it in a separate story.

@model()
class TestModel {
// alternatively use @property.array('array')
@property.array(Array, {
jsonSchema: {
type: 'array',
items: {type: 'string'},
},
})
nestedArr: Array<Array<string>>;
}

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.properties).to.deepEqual({
nestedArr: {
type: 'array',
items: {
type: 'array',
items: {
type: 'string',
},
},
},
});
expectValidJsonSchema(jsonSchema);
});

it('properly converts properties with enum in json schema', () => {
enum QueryLanguage {
JSON = 'json',
Expand Down Expand Up @@ -308,22 +336,20 @@ describe('build-schema', () => {
});
});

it('properly converts properties with recursive arrays', () => {
it('throws for nested array property when json schema is missing', () => {
@model()
class RecursiveArray {
@property.array(Array)
recArr: string[][];
}

const jsonSchema = modelToJsonSchema(RecursiveArray);
expect(jsonSchema.properties).to.eql({
recArr: {
type: 'array',
items: {
type: 'array',
},
expect.throws(
() => {
modelToJsonSchema(RecursiveArray);
},
});
Error,
'You must provide the "jsonSchema" field when define a nested array property',
);
});

it('supports explicit primitive type decoration via strings', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,6 @@ describe('build-schema', () => {
});

describe('metaToJsonSchema', () => {
it('errors out if "itemType" is an array', () => {
expect(() => metaToJsonProperty({type: Array, itemType: []})).to.throw(
/itemType as an array is not supported/,
);
});

it('converts Boolean', () => {
expect(metaToJsonProperty({type: Boolean})).to.eql({
type: 'boolean',
Expand Down
10 changes: 7 additions & 3 deletions packages/repository-json-schema/src/build-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ModelMetadataHelper,
Null,
PropertyDefinition,
PropertyType,
RelationMetadata,
resolveType,
} from '@loopback/repository';
Expand Down Expand Up @@ -242,7 +243,7 @@ export function stringTypeToWrapper(type: string | Function): Function {
* Determines whether a given string or constructor is array type or not
* @param type - Type as string or wrapper
*/
export function isArrayType(type: string | Function) {
export function isArrayType(type: string | Function | PropertyType) {
return type === Array || type === 'array';
}

Expand All @@ -256,8 +257,11 @@ export function metaToJsonProperty(meta: PropertyDefinition): JsonSchema {
let propertyType = meta.type as string | Function;

if (isArrayType(propertyType) && meta.itemType) {
if (Array.isArray(meta.itemType)) {
throw new Error('itemType as an array is not supported');
if (isArrayType(meta.itemType) && !meta.jsonSchema) {
throw new Error(
'You must provide the "jsonSchema" field when define ' +
'a nested array property',
);
}
result = {type: 'array', items: propDef};
propertyType = meta.itemType as string | Function;
Expand Down