diff --git a/packages/filter/src/query.ts b/packages/filter/src/query.ts index f7ad6f3ac8c1..d6150da48337 100644 --- a/packages/filter/src/query.ts +++ b/packages/filter/src/query.ts @@ -230,6 +230,30 @@ export interface Filter { * To include related objects */ include?: InclusionFilter[]; + /** + * return groupBy of + */ + groupBy?: string[]; + /** + * return sum of + */ + sum?: string; + /** + * return min of + */ + min?: string; + /** + * return max of + */ + max?: string; + /** + * return avg of + */ + avg?: string; + /** + * return count of + */ + count?: string; } /** diff --git a/packages/repository-json-schema/src/__tests__/unit/filter-json-schema.unit.ts b/packages/repository-json-schema/src/__tests__/unit/filter-json-schema.unit.ts index 2e30fd2b5dcb..9946432f55f3 100644 --- a/packages/repository-json-schema/src/__tests__/unit/filter-json-schema.unit.ts +++ b/packages/repository-json-schema/src/__tests__/unit/filter-json-schema.unit.ts @@ -72,6 +72,12 @@ describe('getFilterJsonSchemaFor', () => { limit: 10, order: ['id DESC'], skip: 0, + sum: 'salary', + min: 'salary', + max: 'salary', + avg: 'salary', + count: 'salary', + groupBy: ['salary'], }; expectSchemaToAllowFilter(customerFilterSchema, filter); @@ -560,6 +566,9 @@ class Customer extends Entity { @property() name: string; + @property() + salary: number; + @hasMany(() => Order) orders?: Order[]; } diff --git a/packages/repository-json-schema/src/filter-json-schema.ts b/packages/repository-json-schema/src/filter-json-schema.ts index 9ced979baaa7..337b3dcac1c8 100644 --- a/packages/repository-json-schema/src/filter-json-schema.ts +++ b/packages/repository-json-schema/src/filter-json-schema.ts @@ -94,6 +94,32 @@ export function getFilterJsonSchemaFor( examples: [100], }, + sum: { + type: 'string', + examples: ['column1'], + }, + min: { + type: 'string', + examples: ['column1'], + }, + max: { + type: 'string', + examples: ['column1'], + }, + avg: { + type: 'string', + examples: ['column1'], + }, + count: { + type: 'string', + examples: ['column1'], + }, + groupBy: { + type: 'array', + items: { + type: 'string', + }, + }, skip: { type: 'integer', minimum: 0, @@ -120,6 +146,9 @@ export function getFilterJsonSchemaFor( if (!excluded.includes('fields')) { properties.fields = getFieldsJsonSchemaFor(modelCtor, options); } + if (!excluded.includes('groupBy')) { + properties.fields = getGroupByJsonSchemaFor(modelCtor, options); + } // Remove excluded properties for (const p of excluded) { @@ -235,3 +264,37 @@ export function getFieldsJsonSchemaFor( return schema; } + +export function getGroupByJsonSchemaFor( + modelCtor: typeof Model, + options: FilterSchemaOptions = {}, +): JsonSchema { + const schema: JsonSchema = {oneOf: []}; + if (options.setTitle !== false) { + schema.title = `${modelCtor.modelName}.GroupBy`; + } + + const properties = Object.keys(modelCtor.definition.properties); + const additionalProperties = modelCtor.definition.settings.strict === false; + + schema.oneOf?.push({ + type: 'object', + properties: properties.reduce( + (prev, crr) => ({...prev, [crr]: {type: 'boolean'}}), + {}, + ), + additionalProperties, + }); + + schema.oneOf?.push({ + type: 'array', + items: { + type: 'string', + enum: properties.length && !additionalProperties ? properties : undefined, + examples: properties, + }, + uniqueItems: true, + }); + + return schema; +} diff --git a/packages/repository/src/repositories/legacy-juggler-bridge.ts b/packages/repository/src/repositories/legacy-juggler-bridge.ts index 3df52ed95514..2f89a34514a8 100644 --- a/packages/repository/src/repositories/legacy-juggler-bridge.ts +++ b/packages/repository/src/repositories/legacy-juggler-bridge.ts @@ -739,7 +739,7 @@ export class DefaultCrudRepository< } protected toEntity(model: juggler.PersistedModel): R { - return new this.entityClass(model.toObject()) as R; + return new this.entityClass(model.toObject({onlySchema: false})) as R; } protected toEntities(models: juggler.PersistedModel[]): R[] { diff --git a/packages/rest/src/writer.ts b/packages/rest/src/writer.ts index 1236be6bd425..1be18d96249c 100644 --- a/packages/rest/src/writer.ts +++ b/packages/rest/src/writer.ts @@ -50,8 +50,19 @@ export function writeResultToResponse( // TODO(ritch) remove this, should be configurable // See https://github.com/loopbackio/loopback-next/issues/436 response.setHeader('Content-Type', 'application/json'); + let customResult = result; + if (result.length) { + customResult = []; + result.forEach((item: {[key: string]: Object[]}) => { + const org: {[key: string]: Object[]} = {}; + Object.keys(item).forEach(key => { + org[key] = item[key]; + }); + customResult.push(org); + }); + } // TODO(bajtos) handle errors - JSON.stringify can throw - result = JSON.stringify(result); + result = JSON.stringify(customResult); } break; default: