5582 get httpsapitwentycomrestmetadata objects filters dont work (#5906)

- Remove filters from metadata rest api
- add limite before and after parameters for metadata
- remove update from metadata relations
- fix typing issue
- fix naming
- fix before parameter

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
martmull 2024-06-18 18:55:13 +02:00 committed by GitHub
parent dbaa787d19
commit 6fd8dab552
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
87 changed files with 701 additions and 567 deletions

View File

@ -2,7 +2,7 @@ import { Controller, Post, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';
import { RestApiCoreService } from 'src/engine/api/rest/services/rest-api-core.service';
import { RestApiCoreService } from 'src/engine/api/rest/core/rest-api-core.service';
import { cleanGraphQLResponse } from 'src/engine/api/rest/utils/clean-graphql-response.utils';
@Controller('rest/batch/*')

View File

@ -11,7 +11,7 @@ import {
import { Request, Response } from 'express';
import { RestApiCoreService } from 'src/engine/api/rest/services/rest-api-core.service';
import { RestApiCoreService } from 'src/engine/api/rest/core/rest-api-core.service';
import { cleanGraphQLResponse } from 'src/engine/api/rest/utils/clean-graphql-response.utils';
@Controller('rest/*')

View File

@ -2,24 +2,24 @@ import { BadRequestException, Injectable } from '@nestjs/common';
import { Request } from 'express';
import { DeleteQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-query.factory';
import { DeleteQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/delete-query.factory';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { CreateOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-one-query.factory';
import { UpdateQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-query.factory';
import { FindOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-one-query.factory';
import { FindManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-many-query.factory';
import { DeleteVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-variables.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-variables.factory';
import { UpdateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-variables.factory';
import { GetVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/get-variables.factory';
import { parseCorePath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-core-path.utils';
import { computeDepth } from 'src/engine/api/rest/rest-api-core-query-builder/utils/compute-depth.utils';
import { CreateOneQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-one-query.factory';
import { UpdateQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/update-query.factory';
import { FindOneQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-one-query.factory';
import { FindManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-many-query.factory';
import { DeleteVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/delete-variables.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/create-variables.factory';
import { UpdateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/update-variables.factory';
import { GetVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/get-variables.factory';
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
import { computeDepth } from 'src/engine/api/rest/core/query-builder/utils/compute-depth.utils';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { Query } from 'src/engine/api/rest/types/query.type';
import { Query } from 'src/engine/api/rest/core/types/query.type';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { CreateManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-many-query.factory';
import { parseCoreBatchPath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-core-batch-path.utils';
import { CreateManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-many-query.factory';
import { parseCoreBatchPath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-batch-path.utils';
@Injectable()
export class CoreQueryBuilderFactory {

View File

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/rest-api-core-query-builder/core-query-builder.factory';
import { coreQueryBuilderFactories } from 'src/engine/api/rest/rest-api-core-query-builder/factories/factories';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
import { coreQueryBuilderFactories } from 'src/engine/api/rest/core/query-builder/factories/factories';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
@Injectable()
export class CreateManyQueryFactory {

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
@Injectable()
export class CreateOneQueryFactory {

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { QueryVariables } from 'src/engine/api/rest/types/query-variables.type';
import { QueryVariables } from 'src/engine/api/rest/core/types/query-variables.type';
@Injectable()
export class CreateVariablesFactory {

View File

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { QueryVariables } from 'src/engine/api/rest/types/query-variables.type';
import { QueryVariables } from 'src/engine/api/rest/core/types/query-variables.type';
@Injectable()
export class DeleteVariablesFactory {

View File

@ -0,0 +1,25 @@
import { DeleteQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/delete-query.factory';
import { CreateOneQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-one-query.factory';
import { UpdateQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/update-query.factory';
import { FindOneQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-one-query.factory';
import { FindManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-many-query.factory';
import { DeleteVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/delete-variables.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/create-variables.factory';
import { UpdateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/update-variables.factory';
import { GetVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/get-variables.factory';
import { CreateManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-many-query.factory';
import { inputFactories } from 'src/engine/api/rest/input-factories/factories';
export const coreQueryBuilderFactories = [
DeleteQueryFactory,
CreateOneQueryFactory,
CreateManyQueryFactory,
UpdateQueryFactory,
FindOneQueryFactory,
FindManyQueryFactory,
DeleteVariablesFactory,
CreateVariablesFactory,
UpdateVariablesFactory,
GetVariablesFactory,
...inputFactories,
];

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
@Injectable()
export class FindManyQueryFactory {
@ -17,10 +17,16 @@ export class FindManyQueryFactory {
$orderBy: [${objectNameSingular}OrderByInput],
$startingAfter: String,
$endingBefore: String,
$limit: Int = 60
$first: Int,
$last: Int
) {
${objectNamePlural}(
filter: $filter, orderBy: $orderBy, first: $limit, after: $startingAfter, before: $endingBefore
filter: $filter,
orderBy: $orderBy,
first: $first,
last: $last,
after: $startingAfter,
before: $endingBefore
) {
edges {
node {

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
@Injectable()
export class FindOneQueryFactory {

View File

@ -0,0 +1,44 @@
import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory';
import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-input.factory';
import { QueryVariables } from 'src/engine/api/rest/core/types/query-variables.type';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
@Injectable()
export class GetVariablesFactory {
constructor(
private readonly startingAfterInputFactory: StartingAfterInputFactory,
private readonly endingBeforeInputFactory: EndingBeforeInputFactory,
private readonly limitInputFactory: LimitInputFactory,
private readonly orderByInputFactory: OrderByInputFactory,
private readonly filterInputFactory: FilterInputFactory,
) {}
create(
id: string | undefined,
request: Request,
objectMetadata,
): QueryVariables {
if (id) {
return { filter: { id: { eq: id } } };
}
const limit = this.limitInputFactory.create(request);
const endingBefore = this.endingBeforeInputFactory.create(request);
const startingAfter = this.startingAfterInputFactory.create(request);
return {
filter: this.filterInputFactory.create(request, objectMetadata),
orderBy: this.orderByInputFactory.create(request, objectMetadata),
first: !endingBefore ? limit : undefined,
last: endingBefore ? limit : undefined,
startingAfter,
endingBefore,
};
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
@Injectable()
export class UpdateQueryFactory {

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { QueryVariables } from 'src/engine/api/rest/types/query-variables.type';
import { QueryVariables } from 'src/engine/api/rest/core/types/query-variables.type';
@Injectable()
export class UpdateVariablesFactory {

View File

@ -1,6 +1,6 @@
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
import { checkFields } from 'src/engine/api/rest/core/query-builder/utils/check-fields.utils';
import { checkArrayFields } from 'src/engine/api/rest/core/query-builder/utils/check-order-by.utils';
describe('checkFields', () => {
it('should check field types', () => {

View File

@ -1,4 +1,4 @@
import { computeDepth } from 'src/engine/api/rest/rest-api-core-query-builder/utils/compute-depth.utils';
import { computeDepth } from 'src/engine/api/rest/core/query-builder/utils/compute-depth.utils';
describe('computeDepth', () => {
it('should compute depth from query', () => {

View File

@ -1,6 +1,6 @@
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { getFieldType } from 'src/engine/api/rest/rest-api-core-query-builder/utils/get-field-type.utils';
import { getFieldType } from 'src/engine/api/rest/core/query-builder/utils/get-field-type.utils';
describe('getFieldType', () => {
it('should get field type', () => {

View File

@ -5,7 +5,7 @@ import {
fieldTextMock,
objectMetadataItemMock,
} from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/rest-api-core-query-builder/utils/map-field-metadata-to-graphql-query.utils';
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
describe('mapFieldMetadataToGraphqlQuery', () => {
it('should map properly', () => {

View File

@ -1,4 +1,4 @@
import { addDefaultConjunctionIfMissing } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/add-default-conjunction.utils';
import { addDefaultConjunctionIfMissing } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/add-default-conjunction.utils';
describe('addDefaultConjunctionIfMissing', () => {
it('should add default conjunction if missing', () => {

View File

@ -1,4 +1,4 @@
import { checkFilterEnumValues } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/check-filter-enum-values';
import { checkFilterEnumValues } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/check-filter-enum-values';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
fieldSelectMock,

View File

@ -1,4 +1,4 @@
import { checkFilterQuery } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/check-filter-query.utils';
import { checkFilterQuery } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/check-filter-query.utils';
describe('checkFilterQuery', () => {
it('should check filter query', () => {

View File

@ -1,5 +1,5 @@
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { formatFieldValue } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/format-field-values.utils';
import { formatFieldValue } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/format-field-values.utils';
describe('formatFieldValue', () => {
it('should format fieldNumber value', () => {

View File

@ -1,4 +1,4 @@
import { parseBaseFilter } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-base-filter.utils';
import { parseBaseFilter } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-base-filter.utils';
describe('parseBaseFilter', () => {
it('should parse simple filter string test 1', () => {

View File

@ -1,4 +1,4 @@
import { parseFilterContent } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter-content.utils';
import { parseFilterContent } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter-content.utils';
describe('parseFilterContent', () => {
it('should parse query filter test 1', () => {

View File

@ -1,5 +1,5 @@
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { parseFilter } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter.utils';
import { parseFilter } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter.utils';
describe('parseFilter', () => {
it('should parse string filter test 1', () => {

View File

@ -1,4 +1,4 @@
import { Conjunctions } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter.utils';
import { Conjunctions } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter.utils';
export const DEFAULT_CONJUNCTION = Conjunctions.and;

View File

@ -1,7 +1,7 @@
import { BadRequestException } from '@nestjs/common';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FieldValue } from 'src/engine/api/rest/types/field-value.type';
import { FieldValue } from 'src/engine/api/rest/core/types/field-value.type';
export const formatFieldValue = (
value: string,

View File

@ -2,13 +2,13 @@ import { BadRequestException } from '@nestjs/common';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
import { parseFilterContent } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter-content.utils';
import { parseBaseFilter } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-base-filter.utils';
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
import { formatFieldValue } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/format-field-values.utils';
import { FieldValue } from 'src/engine/api/rest/types/field-value.type';
import { checkFilterEnumValues } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/check-filter-enum-values';
import { getFieldType } from 'src/engine/api/rest/rest-api-core-query-builder/utils/get-field-type.utils';
import { parseFilterContent } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter-content.utils';
import { parseBaseFilter } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-base-filter.utils';
import { checkFields } from 'src/engine/api/rest/core/query-builder/utils/check-fields.utils';
import { formatFieldValue } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/format-field-values.utils';
import { FieldValue } from 'src/engine/api/rest/core/types/field-value.type';
import { checkFilterEnumValues } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/check-filter-enum-values';
import { getFieldType } from 'src/engine/api/rest/core/query-builder/utils/get-field-type.utils';
export enum Conjunctions {
or = 'or',

View File

@ -1,4 +1,4 @@
import { parseCoreBatchPath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-core-batch-path.utils';
import { parseCoreBatchPath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-batch-path.utils';
describe('parseCoreBatchPath', () => {
it('should parse object from request path', () => {

View File

@ -1,4 +1,4 @@
import { parseCorePath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-core-path.utils';
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
describe('parseCorePath', () => {
it('should parse object from request path', () => {

View File

@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/rest-api-core-query-builder/core-query-builder.factory';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
import {
GraphqlApiType,
RestApiService,
} from 'src/engine/api/rest/services/rest-api.service';
} from 'src/engine/api/rest/rest-api.service';
@Injectable()
export class RestApiCoreService {

View File

@ -3,7 +3,8 @@ export type QueryVariables = {
data?: object | null;
filter?: object;
orderBy?: object;
limit?: number;
last?: number;
first?: number;
startingAfter?: string;
endingBefore?: string;
input?: object;

View File

@ -0,0 +1,6 @@
import { QueryVariables } from 'src/engine/api/rest/core/types/query-variables.type';
export type Query = {
query: string;
variables: QueryVariables;
};

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/ending-before-input.factory';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
describe('EndingBeforeInputFactory', () => {
let service: EndingBeforeInputFactory;

View File

@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { FilterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-input.factory';
import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-input.factory';
describe('FilterInputFactory', () => {
const objectMetadata = { objectMetadataItem: objectMetadataItemMock };

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LimitInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/limit-input.factory';
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
describe('LimitInputFactory', () => {
let service: LimitInputFactory;

View File

@ -3,7 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { OrderByInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/order-by-input.factory';
import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory';
describe('OrderByInputFactory', () => {
const objectMetadata = { objectMetadataItem: objectMetadataItemMock };

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { StartingAfterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/starting-after-input.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
describe('StartingAfterInputFactory', () => {
let service: StartingAfterInputFactory;

View File

@ -0,0 +1,13 @@
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory';
import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-input.factory';
export const inputFactories = [
StartingAfterInputFactory,
EndingBeforeInputFactory,
LimitInputFactory,
OrderByInputFactory,
FilterInputFactory,
];

View File

@ -2,10 +2,10 @@ import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { addDefaultConjunctionIfMissing } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/add-default-conjunction.utils';
import { checkFilterQuery } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/check-filter-query.utils';
import { parseFilter } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter.utils';
import { FieldValue } from 'src/engine/api/rest/types/field-value.type';
import { addDefaultConjunctionIfMissing } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/add-default-conjunction.utils';
import { checkFilterQuery } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/check-filter-query.utils';
import { parseFilter } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter.utils';
import { FieldValue } from 'src/engine/api/rest/core/types/field-value.type';
@Injectable()
export class FilterInputFactory {

View File

@ -4,9 +4,9 @@ import { Request } from 'express';
@Injectable()
export class LimitInputFactory {
create(request: Request): number {
create(request: Request, defaultLimit = 60): number {
if (!request.query.limit) {
return 60;
return defaultLimit;
}
const limit = +request.query.limit;

View File

@ -7,7 +7,7 @@ import {
RecordOrderBy,
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
import { checkArrayFields } from 'src/engine/api/rest/core/query-builder/utils/check-order-by.utils';
export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst;

View File

@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { fetchMetadataFields } from 'src/engine/api/rest/metadata/query-builder/utils/fetch-metadata-fields.utils';
@Injectable()
export class CreateMetadataQueryFactory {
create(objectNameSingular: string, objectNamePlural: string): string {
const objectNameCapitalized = capitalize(objectNameSingular);
const fields = fetchMetadataFields(objectNamePlural);
return `
mutation Create${objectNameCapitalized}($input: CreateOne${objectNameCapitalized}${
objectNameSingular === 'field' ? 'Metadata' : ''
}Input!) {
createOne${objectNameCapitalized}(input: $input) {
id
${fields}
}
}
`;
}
}

View File

@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
@Injectable()
export class DeleteMetadataQueryFactory {
create(objectNameSingular: string): string {
const objectNameCapitalized = capitalize(objectNameSingular);
return `
mutation Delete${objectNameCapitalized}($input: DeleteOne${objectNameCapitalized}Input!) {
deleteOne${objectNameCapitalized}(input: $input) {
id
}
}
`;
}
}

View File

@ -0,0 +1,33 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { fetchMetadataFields } from 'src/engine/api/rest/metadata/query-builder/utils/fetch-metadata-fields.utils';
@Injectable()
export class FindManyMetadataQueryFactory {
create(objectNamePlural): string {
const fields = fetchMetadataFields(objectNamePlural);
return `
query FindMany${capitalize(objectNamePlural)}(
$paging: CursorPaging!
) {
${objectNamePlural}(
paging: $paging
) {
edges {
node {
id
${fields}
}
}
pageInfo {
hasNextPage
startCursor
endCursor
}
}
}
`;
}
}

View File

@ -0,0 +1,22 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { fetchMetadataFields } from 'src/engine/api/rest/metadata/query-builder/utils/fetch-metadata-fields.utils';
@Injectable()
export class FindOneMetadataQueryFactory {
create(objectNameSingular: string, objectNamePlural: string): string {
const fields = fetchMetadataFields(objectNamePlural);
return `
query FindOne${capitalize(objectNameSingular)}(
$id: UUID!,
) {
${objectNameSingular}(id: $id) {
id
${fields}
}
}
`;
}
}

View File

@ -0,0 +1,42 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Request } from 'express';
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
import { MetadataQueryVariables } from 'src/engine/api/rest/metadata/types/metadata-query-variables.type';
@Injectable()
export class GetMetadataVariablesFactory {
constructor(
private readonly startingAfterInputFactory: StartingAfterInputFactory,
private readonly endingBeforeInputFactory: EndingBeforeInputFactory,
private readonly limitInputFactory: LimitInputFactory,
) {}
create(id: string | undefined, request: Request): MetadataQueryVariables {
if (id) {
return { id };
}
const limit = this.limitInputFactory.create(request, 1000);
const before = this.endingBeforeInputFactory.create(request);
const after = this.startingAfterInputFactory.create(request);
if (before && after) {
throw new BadRequestException(
`Only one of 'endingBefore' and 'startingAfter' may be provided`,
);
}
return {
paging: {
first: !before ? limit : undefined,
last: before ? limit : undefined,
after,
before,
},
};
}
}

View File

@ -0,0 +1,17 @@
import { FindOneMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/find-one-metadata-query.factory';
import { FindManyMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/find-many-metadata-query.factory';
import { GetMetadataVariablesFactory } from 'src/engine/api/rest/metadata/query-builder/factories/get-metadata-variables.factory';
import { inputFactories } from 'src/engine/api/rest/input-factories/factories';
import { CreateMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/create-metadata-query.factory';
import { UpdateMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/update-metadata-query.factory';
import { DeleteMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/delete-metadata-query.factory';
export const metadataQueryBuilderFactories = [
FindOneMetadataQueryFactory,
FindManyMetadataQueryFactory,
CreateMetadataQueryFactory,
DeleteMetadataQueryFactory,
UpdateMetadataQueryFactory,
GetMetadataVariablesFactory,
...inputFactories,
];

View File

@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';
import { capitalize } from 'src/utils/capitalize';
import { fetchMetadataFields } from 'src/engine/api/rest/metadata/query-builder/utils/fetch-metadata-fields.utils';
@Injectable()
export class UpdateMetadataQueryFactory {
create(objectNameSingular: string, objectNamePlural: string): string {
const objectNameCapitalized = capitalize(objectNameSingular);
const fields = fetchMetadataFields(objectNamePlural);
return `
mutation Update${objectNameCapitalized}($input: UpdateOne${objectNameCapitalized}${
objectNameSingular === 'field' ? 'Metadata' : ''
}Input!) {
updateOne${objectNameCapitalized}(input: $input) {
id
${fields}
}
}
`;
}
}

View File

@ -0,0 +1,95 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Request } from 'express';
import { GetMetadataVariablesFactory } from 'src/engine/api/rest/metadata/query-builder/factories/get-metadata-variables.factory';
import { FindOneMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/find-one-metadata-query.factory';
import { FindManyMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/find-many-metadata-query.factory';
import { parseMetadataPath } from 'src/engine/api/rest/metadata/query-builder/utils/parse-metadata-path.utils';
import { CreateMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/create-metadata-query.factory';
import { UpdateMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/update-metadata-query.factory';
import { DeleteMetadataQueryFactory } from 'src/engine/api/rest/metadata/query-builder/factories/delete-metadata-query.factory';
import { MetadataQuery } from 'src/engine/api/rest/metadata/types/metadata-query.type';
@Injectable()
export class MetadataQueryBuilderFactory {
constructor(
private readonly findOneQueryFactory: FindOneMetadataQueryFactory,
private readonly findManyQueryFactory: FindManyMetadataQueryFactory,
private readonly createQueryFactory: CreateMetadataQueryFactory,
private readonly updateQueryFactory: UpdateMetadataQueryFactory,
private readonly deleteQueryFactory: DeleteMetadataQueryFactory,
private readonly getMetadataVariablesFactory: GetMetadataVariablesFactory,
) {}
async get(request: Request): Promise<MetadataQuery> {
const { id, objectNameSingular, objectNamePlural } =
parseMetadataPath(request);
return {
query: id
? this.findOneQueryFactory.create(objectNameSingular, objectNamePlural)
: this.findManyQueryFactory.create(objectNamePlural),
variables: this.getMetadataVariablesFactory.create(id, request),
};
}
async create(request: Request): Promise<MetadataQuery> {
const { objectNameSingular, objectNamePlural } = parseMetadataPath(request);
return {
query: this.createQueryFactory.create(
objectNameSingular,
objectNamePlural,
),
variables: {
input: {
[objectNameSingular]: request.body,
},
},
};
}
async update(request: Request): Promise<MetadataQuery> {
const { objectNameSingular, objectNamePlural, id } =
parseMetadataPath(request);
if (!id) {
throw new BadRequestException(
`update ${objectNameSingular} query invalid. Id missing. eg: /rest/metadata/${objectNameSingular}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
);
}
return {
query: this.updateQueryFactory.create(
objectNameSingular,
objectNamePlural,
),
variables: {
input: {
update: request.body,
id,
},
},
};
}
async delete(request: Request): Promise<MetadataQuery> {
const { objectNameSingular, id } = parseMetadataPath(request);
if (!id) {
throw new BadRequestException(
`delete ${objectNameSingular} query invalid. Id missing. eg: /rest/metadata/${objectNameSingular}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
);
}
return {
query: this.deleteQueryFactory.create(objectNameSingular),
variables: {
input: {
id,
},
},
};
}
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { MetadataQueryBuilderFactory } from 'src/engine/api/rest/metadata/query-builder/metadata-query-builder.factory';
import { metadataQueryBuilderFactories } from 'src/engine/api/rest/metadata/query-builder/factories/metadata-factories';
@Module({
imports: [AuthModule],
providers: [...metadataQueryBuilderFactories, MetadataQueryBuilderFactory],
exports: [MetadataQueryBuilderFactory],
})
export class MetadataQueryBuilderModule {}

View File

@ -1,4 +1,4 @@
import { parseMetadataPath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-metadata-path.utils';
import { parseMetadataPath } from 'src/engine/api/rest/metadata/query-builder/utils/parse-metadata-path.utils';
describe('parseMetadataPath', () => {
it('should parse object from request path with uuid', () => {

View File

@ -0,0 +1,93 @@
export const fetchMetadataFields = (objectNamePlural: string) => {
const fields = `
type
name
label
description
icon
isCustom
isActive
isSystem
isNullable
createdAt
updatedAt
fromRelationMetadata {
id
relationType
toObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
toFieldMetadataId
}
toRelationMetadata {
id
relationType
fromObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
fromFieldMetadataId
}
defaultValue
options
`;
switch (objectNamePlural) {
case 'objects':
return `
dataSourceId
nameSingular
namePlural
labelSingular
labelPlural
description
icon
isCustom
isActive
isSystem
createdAt
updatedAt
labelIdentifierFieldMetadataId
imageIdentifierFieldMetadataId
fields(paging: { first: 1000 }) {
edges {
node {
id
${fields}
}
}
}
`;
case 'fields':
return fields;
case 'relations':
return `
relationType
fromObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
fromObjectMetadataId
toObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
toObjectMetadataId
fromFieldMetadataId
toFieldMetadataId
`;
}
};

View File

@ -11,7 +11,7 @@ import {
import { Request, Response } from 'express';
import { RestApiMetadataService } from 'src/engine/api/rest/services/rest-api-metadata.service';
import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-metadata.service';
import { cleanGraphQLResponse } from 'src/engine/api/rest/utils/clean-graphql-response.utils';
@Controller('rest/metadata/*')

View File

@ -0,0 +1,63 @@
import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import {
GraphqlApiType,
RestApiService,
} from 'src/engine/api/rest/rest-api.service';
import { MetadataQueryBuilderFactory } from 'src/engine/api/rest/metadata/query-builder/metadata-query-builder.factory';
@Injectable()
export class RestApiMetadataService {
constructor(
private readonly tokenService: TokenService,
private readonly metadataQueryBuilderFactory: MetadataQueryBuilderFactory,
private readonly restApiService: RestApiService,
) {}
async get(request: Request) {
await this.tokenService.validateToken(request);
const data = await this.metadataQueryBuilderFactory.get(request);
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async create(request: Request) {
await this.tokenService.validateToken(request);
const data = await this.metadataQueryBuilderFactory.create(request);
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async update(request: Request) {
await this.tokenService.validateToken(request);
const data = await this.metadataQueryBuilderFactory.update(request);
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async delete(request: Request) {
await this.tokenService.validateToken(request);
const data = await this.metadataQueryBuilderFactory.delete(request);
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
}

View File

@ -0,0 +1,10 @@
export type MetadataQueryVariables = {
id?: string;
input?: object;
paging?: {
first?: number;
last?: number;
after?: string;
before?: string;
};
};

View File

@ -0,0 +1,6 @@
import { MetadataQueryVariables } from 'src/engine/api/rest/metadata/types/metadata-query-variables.type';
export type MetadataQuery = {
query: string;
variables: MetadataQueryVariables;
};

View File

@ -1,46 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/rest-api-core-query-builder/core-query-builder.factory';
import { DeleteQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-query.factory';
import { CreateOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-one-query.factory';
import { CreateManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-many-query.factory';
import { UpdateQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-query.factory';
import { FindOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-one-query.factory';
import { FindManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-many-query.factory';
import { DeleteVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-variables.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-variables.factory';
import { UpdateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-variables.factory';
import { GetVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/get-variables.factory';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
describe('CoreQueryBuilderFactory', () => {
let service: CoreQueryBuilderFactory;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CoreQueryBuilderFactory,
{ provide: DeleteQueryFactory, useValue: {} },
{ provide: CreateOneQueryFactory, useValue: {} },
{ provide: CreateManyQueryFactory, useValue: {} },
{ provide: UpdateQueryFactory, useValue: {} },
{ provide: FindOneQueryFactory, useValue: {} },
{ provide: FindManyQueryFactory, useValue: {} },
{ provide: DeleteVariablesFactory, useValue: {} },
{ provide: CreateVariablesFactory, useValue: {} },
{ provide: UpdateVariablesFactory, useValue: {} },
{ provide: GetVariablesFactory, useValue: {} },
{ provide: ObjectMetadataService, useValue: {} },
{ provide: TokenService, useValue: {} },
{ provide: EnvironmentService, useValue: {} },
],
}).compile();
service = module.get<CoreQueryBuilderFactory>(CoreQueryBuilderFactory);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -1,33 +0,0 @@
import { DeleteQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-query.factory';
import { CreateOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-one-query.factory';
import { UpdateQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-query.factory';
import { FindOneQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-one-query.factory';
import { FindManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/find-many-query.factory';
import { DeleteVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/delete-variables.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-variables.factory';
import { UpdateVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/update-variables.factory';
import { GetVariablesFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/get-variables.factory';
import { LimitInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/limit-input.factory';
import { OrderByInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/order-by-input.factory';
import { FilterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-input.factory';
import { CreateManyQueryFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/create-many-query.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/starting-after-input.factory';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/ending-before-input.factory';
export const coreQueryBuilderFactories = [
DeleteQueryFactory,
CreateOneQueryFactory,
CreateManyQueryFactory,
UpdateQueryFactory,
FindOneQueryFactory,
FindManyQueryFactory,
DeleteVariablesFactory,
CreateVariablesFactory,
UpdateVariablesFactory,
GetVariablesFactory,
StartingAfterInputFactory,
EndingBeforeInputFactory,
LimitInputFactory,
OrderByInputFactory,
FilterInputFactory,
];

View File

@ -1,39 +0,0 @@
import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { LimitInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/limit-input.factory';
import { OrderByInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/order-by-input.factory';
import { FilterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-input.factory';
import { QueryVariables } from 'src/engine/api/rest/types/query-variables.type';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/ending-before-input.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/starting-after-input.factory';
@Injectable()
export class GetVariablesFactory {
constructor(
private readonly startingAfterInputFactory: StartingAfterInputFactory,
private readonly endingBeforeInputFactory: EndingBeforeInputFactory,
private readonly limitInputFactory: LimitInputFactory,
private readonly orderByInputFactory: OrderByInputFactory,
private readonly filterInputFactory: FilterInputFactory,
) {}
create(
id: string | undefined,
request: Request,
objectMetadata,
): QueryVariables {
if (id) {
return { filter: { id: { eq: id } } };
}
return {
filter: this.filterInputFactory.create(request, objectMetadata),
orderBy: this.orderByInputFactory.create(request, objectMetadata),
limit: this.limitInputFactory.create(request),
startingAfter: this.startingAfterInputFactory.create(request),
endingBefore: this.endingBeforeInputFactory.create(request),
};
}
}

View File

@ -1,23 +1,39 @@
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { RestApiCoreController } from 'src/engine/api/rest/controllers/rest-api-core.controller';
import { RestApiCoreService } from 'src/engine/api/rest/services/rest-api-core.service';
import { CoreQueryBuilderModule } from 'src/engine/api/rest/rest-api-core-query-builder/core-query-builder.module';
import { RestApiCoreController } from 'src/engine/api/rest/core/controllers/rest-api-core.controller';
import { RestApiCoreService } from 'src/engine/api/rest/core/rest-api-core.service';
import { CoreQueryBuilderModule } from 'src/engine/api/rest/core/query-builder/core-query-builder.module';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { RestApiMetadataController } from 'src/engine/api/rest/controllers/rest-api-metadata.controller';
import { RestApiMetadataService } from 'src/engine/api/rest/services/rest-api-metadata.service';
import { RestApiCoreBatchController } from 'src/engine/api/rest/controllers/rest-api-core-batch.controller';
import { RestApiService } from 'src/engine/api/rest/services/rest-api.service';
import { RestApiMetadataController } from 'src/engine/api/rest/metadata/rest-api-metadata.controller';
import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-metadata.service';
import { RestApiCoreBatchController } from 'src/engine/api/rest/core/controllers/rest-api-core-batch.controller';
import { RestApiService } from 'src/engine/api/rest/rest-api.service';
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
import { MetadataQueryBuilderModule } from 'src/engine/api/rest/metadata/query-builder/metadata-query-builder.module';
@Module({
imports: [CoreQueryBuilderModule, AuthModule, HttpModule],
imports: [
CoreQueryBuilderModule,
MetadataQueryBuilderModule,
AuthModule,
HttpModule,
],
controllers: [
RestApiMetadataController,
RestApiCoreBatchController,
RestApiCoreController,
],
providers: [RestApiMetadataService, RestApiCoreService, RestApiService],
providers: [
RestApiMetadataService,
RestApiCoreService,
RestApiService,
StartingAfterInputFactory,
EndingBeforeInputFactory,
LimitInputFactory,
],
exports: [RestApiMetadataService],
})
export class RestApiModule {}

View File

@ -4,7 +4,7 @@ import { HttpService } from '@nestjs/axios';
import { Request } from 'express';
import { AxiosResponse } from 'axios';
import { Query } from 'src/engine/api/rest/types/query.type';
import { Query } from 'src/engine/api/rest/core/types/query.type';
import { getServerUrl } from 'src/utils/get-server-url';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { RestApiException } from 'src/engine/api/rest/errors/RestApiException';

View File

@ -1,30 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RestApiCoreService } from 'src/engine/api/rest/services/rest-api-core.service';
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/rest-api-core-query-builder/core-query-builder.factory';
import { RestApiService } from 'src/engine/api/rest/services/rest-api.service';
describe('RestApiCoreService', () => {
let service: RestApiCoreService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
RestApiCoreService,
{
provide: CoreQueryBuilderFactory,
useValue: {},
},
{
provide: RestApiService,
useValue: {},
},
],
}).compile();
service = module.get<RestApiCoreService>(RestApiCoreService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -1,290 +0,0 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Query } from 'src/engine/api/rest/types/query.type';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { capitalize } from 'src/utils/capitalize';
import { parseMetadataPath } from 'src/engine/api/rest/rest-api-core-query-builder/utils/path-parsers/parse-metadata-path.utils';
import {
GraphqlApiType,
RestApiService,
} from 'src/engine/api/rest/services/rest-api.service';
@Injectable()
export class RestApiMetadataService {
constructor(
private readonly tokenService: TokenService,
private readonly restApiService: RestApiService,
) {}
fetchMetadataFields(objectNamePlural: string) {
const fields = `
type
name
label
description
icon
isCustom
isActive
isSystem
isNullable
createdAt
updatedAt
fromRelationMetadata {
id
relationType
toObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
toFieldMetadataId
}
toRelationMetadata {
id
relationType
fromObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
fromFieldMetadataId
}
defaultValue
options
`;
switch (objectNamePlural) {
case 'objects':
return `
dataSourceId
nameSingular
namePlural
labelSingular
labelPlural
description
icon
isCustom
isActive
isSystem
createdAt
updatedAt
labelIdentifierFieldMetadataId
imageIdentifierFieldMetadataId
fields(paging: { first: 1000 }) {
edges {
node {
id
${fields}
}
}
}
`;
case 'fields':
return fields;
case 'relations':
return `
relationType
fromObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
fromObjectMetadataId
toObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
toObjectMetadataId
fromFieldMetadataId
toFieldMetadataId
`;
}
}
generateFindManyQuery(objectNameSingular: string, objectNamePlural: string) {
const fields = this.fetchMetadataFields(objectNamePlural);
let filterType = '';
let filterValue = '';
if (objectNamePlural !== 'relations') {
filterType = `($filter: ${objectNameSingular}Filter)`;
filterValue = 'filter: $filter,';
}
return `
query FindMany${capitalize(objectNamePlural)}${filterType} {
${objectNamePlural}(
${filterValue}
paging: { first: 1000 }
) {
edges {
node {
id
${fields}
}
}
}
}
`;
}
generateFindOneQuery(objectNameSingular: string, objectNamePlural: string) {
const fields = this.fetchMetadataFields(objectNamePlural);
return `
query FindOne${capitalize(objectNameSingular)}(
$id: UUID!,
) {
${objectNameSingular}(id: $id) {
id
${fields}
}
}
`;
}
async get(request) {
await this.tokenService.validateToken(request);
const { objectNameSingular, objectNamePlural, id } =
parseMetadataPath(request);
const query = id
? this.generateFindOneQuery(objectNameSingular, objectNamePlural)
: this.generateFindManyQuery(objectNameSingular, objectNamePlural);
const data: Query = {
query,
variables: id ? { id } : request.body,
};
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async create(request) {
await this.tokenService.validateToken(request);
const { objectNameSingular: objectName, objectNamePlural } =
parseMetadataPath(request);
const objectNameCapitalized = capitalize(objectName);
const fields = this.fetchMetadataFields(objectNamePlural);
const query = `
mutation Create${objectNameCapitalized}($input: CreateOne${objectNameCapitalized}Input!) {
createOne${objectNameCapitalized}(input: $input) {
id
${fields}
}
}
`;
const data: Query = {
query,
variables: {
input: {
[objectName]: request.body,
},
},
};
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async update(request) {
await this.tokenService.validateToken(request);
const {
objectNameSingular: objectName,
objectNamePlural,
id,
} = parseMetadataPath(request);
const objectNameCapitalized = capitalize(objectName);
if (!id) {
throw new BadRequestException(
`update ${objectName} query invalid. Id missing. eg: /rest/metadata/${objectName}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
);
}
const fields = this.fetchMetadataFields(objectNamePlural);
const query = `
mutation Update${objectNameCapitalized}($input: UpdateOne${objectNameCapitalized}Input!) {
updateOne${objectNameCapitalized}(input: $input) {
id
${fields}
}
}
`;
const data: Query = {
query,
variables: {
input: {
update: request.body,
id,
},
},
};
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
async delete(request) {
await this.tokenService.validateToken(request);
const { objectNameSingular: objectName, id } = parseMetadataPath(request);
const objectNameCapitalized = capitalize(objectName);
if (!id) {
throw new BadRequestException(
`delete ${objectName} query invalid. Id missing. eg: /rest/metadata/${objectName}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
);
}
const query = `
mutation Delete${objectNameCapitalized}($input: DeleteOne${objectNameCapitalized}Input!) {
deleteOne${objectNameCapitalized}(input: $input) {
id
}
}
`;
const data: Query = {
query,
variables: {
input: {
id,
},
},
};
return await this.restApiService.call(
GraphqlApiType.METADATA,
request,
data,
);
}
}

View File

@ -1,6 +0,0 @@
import { QueryVariables } from 'src/engine/api/rest/types/query-variables.type';
export type Query = {
query: string;
variables: QueryVariables;
};

View File

@ -133,12 +133,13 @@ export class OpenApiService {
get: {
tags: [item.namePlural],
summary: `Find Many ${item.namePlural}`,
parameters:
item.namePlural !== 'relations'
? [{ $ref: '#/components/parameters/filter' }]
: undefined,
parameters: [
{ $ref: '#/components/parameters/limit' },
{ $ref: '#/components/parameters/startingAfter' },
{ $ref: '#/components/parameters/endingBefore' },
],
responses: {
'200': getFindManyResponse200(item),
'200': getFindManyResponse200(item, true),
'400': { $ref: '#/components/responses/400' },
'401': { $ref: '#/components/responses/401' },
},
@ -161,7 +162,7 @@ export class OpenApiService {
summary: `Find One ${item.nameSingular}`,
parameters: [{ $ref: '#/components/parameters/idPath' }],
responses: {
'200': getFindOneResponse200(item),
'200': getFindOneResponse200(item, true),
'400': { $ref: '#/components/responses/400' },
'401': { $ref: '#/components/responses/401' },
},
@ -177,9 +178,10 @@ export class OpenApiService {
'401': { $ref: '#/components/responses/401' },
},
},
...(item.nameSingular !== 'relation' && {
patch: {
tags: [item.namePlural],
summary: `Update One ${item.namePlural}`,
summary: `Update One ${item.nameSingular}`,
operationId: `updateOne${capitalize(item.nameSingular)}`,
parameters: [{ $ref: '#/components/parameters/idPath' }],
requestBody: getRequestBody(capitalize(item.nameSingular)),
@ -189,6 +191,7 @@ export class OpenApiService {
'401': { $ref: '#/components/responses/401' },
},
},
}),
} as OpenAPIV3_1.PathItemObject;
return path;
@ -197,7 +200,7 @@ export class OpenApiService {
schema.components = {
...schema.components, // components.securitySchemes is defined in base Schema
schemas: computeMetadataSchemaComponents(metadata),
parameters: computeParameterComponents(),
parameters: computeParameterComponents(true),
responses: {
'400': get400ErrorResponses(),
'401': get401ErrorResponses(),

View File

@ -9,10 +9,10 @@ import {
computeOrderByParameters,
computeStartingAfterParameters,
} from 'src/engine/core-modules/open-api/utils/parameters.utils';
import { DEFAULT_ORDER_DIRECTION } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/order-by-input.factory';
import { FilterComparators } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-base-filter.utils';
import { Conjunctions } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter.utils';
import { DEFAULT_CONJUNCTION } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/add-default-conjunction.utils';
import { DEFAULT_ORDER_DIRECTION } from 'src/engine/api/rest/input-factories/order-by-input.factory';
import { FilterComparators } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-base-filter.utils';
import { Conjunctions } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter.utils';
import { DEFAULT_CONJUNCTION } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/add-default-conjunction.utils';
describe('computeParameters', () => {
describe('computeLimit', () => {

View File

@ -218,10 +218,9 @@ export const computeSchemaComponents = (
);
};
export const computeParameterComponents = (): Record<
string,
OpenAPIV3_1.ParameterObject
> => {
export const computeParameterComponents = (
fromMetadata = false,
): Record<string, OpenAPIV3_1.ParameterObject> => {
return {
idPath: computeIdPathParameter(),
startingAfter: computeStartingAfterParameters(),
@ -229,7 +228,7 @@ export const computeParameterComponents = (): Record<
filter: computeFilterParameters(),
depth: computeDepthParameters(),
orderBy: computeOrderByParameters(),
limit: computeLimitParameters(),
limit: computeLimitParameters(fromMetadata),
};
};
@ -240,7 +239,7 @@ export const computeMetadataSchemaComponents = (
(schemas, item) => {
switch (item.nameSingular) {
case 'object': {
schemas[`${capitalize(item.nameSingular)} with Relations`] = {
schemas[`${capitalize(item.nameSingular)}`] = {
type: 'object',
description: `An object`,
properties: {
@ -290,7 +289,7 @@ export const computeMetadataSchemaComponents = (
return schemas;
}
case 'field': {
schemas[`${capitalize(item.nameSingular)} with Relations`] = {
schemas[`${capitalize(item.nameSingular)}`] = {
type: 'object',
description: `A field`,
properties: {
@ -358,7 +357,7 @@ export const computeMetadataSchemaComponents = (
return schemas;
}
case 'relation': {
schemas[`${capitalize(item.nameSingular)} with Relations`] = {
schemas[`${capitalize(item.nameSingular)}`] = {
type: 'object',
description: 'A relation',
properties: {

View File

@ -2,12 +2,14 @@ import { OpenAPIV3_1 } from 'openapi-types';
import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
import { FilterComparators } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-base-filter.utils';
import { Conjunctions } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/parse-filter.utils';
import { DEFAULT_ORDER_DIRECTION } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/order-by-input.factory';
import { DEFAULT_CONJUNCTION } from 'src/engine/api/rest/rest-api-core-query-builder/factories/input-factories/filter-utils/add-default-conjunction.utils';
import { FilterComparators } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-base-filter.utils';
import { Conjunctions } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/parse-filter.utils';
import { DEFAULT_ORDER_DIRECTION } from 'src/engine/api/rest/input-factories/order-by-input.factory';
import { DEFAULT_CONJUNCTION } from 'src/engine/api/rest/core/query-builder/utils/filter-utils/add-default-conjunction.utils';
export const computeLimitParameters = (): OpenAPIV3_1.ParameterObject => {
export const computeLimitParameters = (
fromMetadata = false,
): OpenAPIV3_1.ParameterObject => {
return {
name: 'limit',
in: 'query',
@ -16,8 +18,8 @@ export const computeLimitParameters = (): OpenAPIV3_1.ParameterObject => {
schema: {
type: 'integer',
minimum: 0,
maximum: 60,
default: 60,
maximum: fromMetadata ? 1000 : 60,
default: fromMetadata ? 1000 : 60,
},
};
};

View File

@ -105,7 +105,7 @@ export const computeSingleResultPath = (
},
patch: {
tags: [item.namePlural],
summary: `Update One ${item.namePlural}`,
summary: `Update One ${item.nameSingular}`,
operationId: `UpdateOne${capitalize(item.nameSingular)}`,
parameters: [
{ $ref: '#/components/parameters/idPath' },

View File

@ -3,6 +3,7 @@ import { capitalize } from 'src/utils/capitalize';
export const getFindManyResponse200 = (
item: Pick<ObjectMetadataEntity, 'nameSingular' | 'namePlural'>,
fromMetadata = false,
) => {
return {
description: 'Successful operation',
@ -19,7 +20,7 @@ export const getFindManyResponse200 = (
items: {
$ref: `#/components/schemas/${capitalize(
item.nameSingular,
)} with Relations`,
)}${!fromMetadata ? ' with Relations' : ''}`,
},
},
},
@ -32,9 +33,11 @@ export const getFindManyResponse200 = (
endCursor: { type: 'string' },
},
},
...(!fromMetadata && {
totalCount: {
type: 'integer',
},
}),
},
example: {
data: {
@ -59,6 +62,7 @@ export const getFindManyResponse200 = (
export const getFindOneResponse200 = (
item: Pick<ObjectMetadataEntity, 'nameSingular'>,
fromMetadata = false,
) => {
return {
description: 'Successful operation',
@ -71,9 +75,9 @@ export const getFindOneResponse200 = (
type: 'object',
properties: {
[item.nameSingular]: {
$ref: `#/components/schemas/${capitalize(
item.nameSingular,
)} with Relations`,
$ref: `#/components/schemas/${capitalize(item.nameSingular)}${
!fromMetadata ? ' with Relations' : ''
}`,
},
},
},