mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-25 04:55:30 +03:00
Throw an error if workspace id has no object (#2857)
* Throw an error if workspace id has no object * Request only plurial object names * Fix tests * Fix query * Handle graphql errors
This commit is contained in:
parent
06936c3c2a
commit
3cd1ec21e6
@ -27,6 +27,7 @@ import OptionTable from '@site/src/theme/OptionTable'
|
|||||||
['REDIS_HOST', '127.0.0.1', 'Redis connection host'],
|
['REDIS_HOST', '127.0.0.1', 'Redis connection host'],
|
||||||
['REDIS_PORT', '6379', 'Redis connection port'],
|
['REDIS_PORT', '6379', 'Redis connection port'],
|
||||||
['FRONT_BASE_URL', 'http://localhost:3001', 'Url to the hosted frontend'],
|
['FRONT_BASE_URL', 'http://localhost:3001', 'Url to the hosted frontend'],
|
||||||
|
['SERVER_URL', 'http://localhost:3000', 'Url to the hosted server'],
|
||||||
['PORT', '3000', 'Port'],
|
['PORT', '3000', 'Port'],
|
||||||
]}></OptionTable>
|
]}></OptionTable>
|
||||||
|
|
||||||
@ -97,4 +98,4 @@ import OptionTable from '@site/src/theme/OptionTable'
|
|||||||
<OptionTable options={[
|
<OptionTable options={[
|
||||||
['DEBUG_MODE', 'true', 'Activate debug mode'],
|
['DEBUG_MODE', 'true', 'Activate debug mode'],
|
||||||
['SIGN_IN_PREFILLED', 'true', 'Prefill the Signin form for usage in a demo or dev enviroment'],
|
['SIGN_IN_PREFILLED', 'true', 'Prefill the Signin form for usage in a demo or dev enviroment'],
|
||||||
]}></OptionTable>
|
]}></OptionTable>
|
||||||
|
@ -30,3 +30,4 @@ SIGN_IN_PREFILLED=true
|
|||||||
# REDIS_HOST=127.0.0.1
|
# REDIS_HOST=127.0.0.1
|
||||||
# REDIS_PORT=6379
|
# REDIS_PORT=6379
|
||||||
# DEMO_WORKSPACE_IDS=REPLACE_ME_WITH_A_RANDOM_UUID
|
# DEMO_WORKSPACE_IDS=REPLACE_ME_WITH_A_RANDOM_UUID
|
||||||
|
# SERVER_URL=http://localhost:3000
|
||||||
|
@ -12,6 +12,7 @@ import { UpdateVariablesFactory } from 'src/core/api-rest/api-rest-query-builder
|
|||||||
import { GetVariablesFactory } from 'src/core/api-rest/api-rest-query-builder/factories/get-variables.factory';
|
import { GetVariablesFactory } from 'src/core/api-rest/api-rest-query-builder/factories/get-variables.factory';
|
||||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
import { TokenService } from 'src/core/auth/services/token.service';
|
import { TokenService } from 'src/core/auth/services/token.service';
|
||||||
|
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||||
|
|
||||||
describe('ApiRestQueryBuilderFactory', () => {
|
describe('ApiRestQueryBuilderFactory', () => {
|
||||||
let service: ApiRestQueryBuilderFactory;
|
let service: ApiRestQueryBuilderFactory;
|
||||||
@ -31,6 +32,7 @@ describe('ApiRestQueryBuilderFactory', () => {
|
|||||||
{ provide: GetVariablesFactory, useValue: {} },
|
{ provide: GetVariablesFactory, useValue: {} },
|
||||||
{ provide: ObjectMetadataService, useValue: {} },
|
{ provide: ObjectMetadataService, useValue: {} },
|
||||||
{ provide: TokenService, useValue: {} },
|
{ provide: TokenService, useValue: {} },
|
||||||
|
{ provide: EnvironmentService, useValue: {} },
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import { parsePath } from 'src/core/api-rest/api-rest-query-builder/utils/parse-
|
|||||||
import { computeDepth } from 'src/core/api-rest/api-rest-query-builder/utils/compute-depth.utils';
|
import { computeDepth } from 'src/core/api-rest/api-rest-query-builder/utils/compute-depth.utils';
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import { ApiRestQuery } from 'src/core/api-rest/types/api-rest-query.type';
|
import { ApiRestQuery } from 'src/core/api-rest/types/api-rest-query.type';
|
||||||
|
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApiRestQueryBuilderFactory {
|
export class ApiRestQueryBuilderFactory {
|
||||||
@ -32,6 +33,7 @@ export class ApiRestQueryBuilderFactory {
|
|||||||
private readonly getVariablesFactory: GetVariablesFactory,
|
private readonly getVariablesFactory: GetVariablesFactory,
|
||||||
private readonly objectMetadataService: ObjectMetadataService,
|
private readonly objectMetadataService: ObjectMetadataService,
|
||||||
private readonly tokenService: TokenService,
|
private readonly tokenService: TokenService,
|
||||||
|
private readonly environmentService: EnvironmentService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getObjectMetadata(request: Request): Promise<{
|
async getObjectMetadata(request: Request): Promise<{
|
||||||
@ -43,29 +45,27 @@ export class ApiRestQueryBuilderFactory {
|
|||||||
const objectMetadataItems =
|
const objectMetadataItems =
|
||||||
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||||
|
|
||||||
const { id, object: parsedObject } = parsePath(request);
|
if (!objectMetadataItems.length) {
|
||||||
|
throw new BadRequestException(
|
||||||
let objectNameKey = 'namePlural';
|
`No object was found for the workspace associated with this API key. You may generate a new one here ${this.environmentService.getFrontBaseUrl()}/settings/developers/api-keys`,
|
||||||
let wrongObjectNameKey = 'nameSingular';
|
);
|
||||||
|
|
||||||
if (id) {
|
|
||||||
objectNameKey = 'nameSingular';
|
|
||||||
wrongObjectNameKey = 'namePlural';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { object: parsedObject } = parsePath(request);
|
||||||
|
|
||||||
const [objectMetadata] = objectMetadataItems.filter(
|
const [objectMetadata] = objectMetadataItems.filter(
|
||||||
(object) => object[objectNameKey] === parsedObject,
|
(object) => object.namePlural === parsedObject,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!objectMetadata) {
|
if (!objectMetadata) {
|
||||||
const [wrongObjectMetadata] = objectMetadataItems.filter(
|
const [wrongObjectMetadata] = objectMetadataItems.filter(
|
||||||
(object) => object[wrongObjectNameKey] === parsedObject,
|
(object) => object.nameSingular === parsedObject,
|
||||||
);
|
);
|
||||||
|
|
||||||
let hint = 'eg: companies';
|
let hint = 'eg: companies';
|
||||||
|
|
||||||
if (wrongObjectMetadata) {
|
if (wrongObjectMetadata) {
|
||||||
hint = `Did you mean '${wrongObjectMetadata[objectNameKey]}'?`;
|
hint = `Did you mean '${wrongObjectMetadata.namePlural}'?`;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
|
@ -7,7 +7,7 @@ import { ApiRestResponse } from 'src/core/api-rest/types/api-rest-response.type'
|
|||||||
|
|
||||||
const handleResult = (res: Response, result: ApiRestResponse) => {
|
const handleResult = (res: Response, result: ApiRestResponse) => {
|
||||||
if (result.data.error) {
|
if (result.data.error) {
|
||||||
res.status(400).send(result.data);
|
res.status(result.data.status || 400).send({ error: result.data.error });
|
||||||
} else {
|
} else {
|
||||||
res.send(result.data);
|
res.send(result.data);
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,15 @@ export class ApiRestService {
|
|||||||
request: Request,
|
request: Request,
|
||||||
data: ApiRestQuery,
|
data: ApiRestQuery,
|
||||||
): Promise<ApiRestResponse> {
|
): Promise<ApiRestResponse> {
|
||||||
return await axios.post(
|
const baseUrl =
|
||||||
`${request.protocol}://${request.get('host')}/graphql`,
|
this.environmentService.getServerUrl() ||
|
||||||
data,
|
`${request.protocol}://${request.get('host')}`;
|
||||||
{
|
|
||||||
headers: {
|
return await axios.post(`${baseUrl}/graphql`, data, {
|
||||||
Authorization: request.headers.authorization,
|
headers: {
|
||||||
},
|
Authorization: request.headers.authorization,
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(request: Request): Promise<ApiRestResponse> {
|
async get(request: Request): Promise<ApiRestResponse> {
|
||||||
@ -38,7 +38,7 @@ export class ApiRestService {
|
|||||||
|
|
||||||
return await this.callGraphql(request, data);
|
return await this.callGraphql(request, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return { data: { error: `${err}` } };
|
return { data: { error: `${err}`, status: err.response.status } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
export type ApiRestResponse = { data: { error?: string } };
|
export type ApiRestResponse = { data: { error?: string; status?: number } };
|
||||||
|
@ -50,6 +50,10 @@ export class EnvironmentService {
|
|||||||
return this.configService.get<string>('FRONT_BASE_URL')!;
|
return this.configService.get<string>('FRONT_BASE_URL')!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getServerUrl(): string {
|
||||||
|
return this.configService.get<string>('SERVER_URL')!;
|
||||||
|
}
|
||||||
|
|
||||||
getAccessTokenSecret(): string {
|
getAccessTokenSecret(): string {
|
||||||
return this.configService.get<string>('ACCESS_TOKEN_SECRET')!;
|
return this.configService.get<string>('ACCESS_TOKEN_SECRET')!;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,11 @@ export class EnvironmentVariables {
|
|||||||
@IsUrl({ require_tld: false })
|
@IsUrl({ require_tld: false })
|
||||||
FRONT_BASE_URL: string;
|
FRONT_BASE_URL: string;
|
||||||
|
|
||||||
|
// Frontend URL
|
||||||
|
@IsUrl({ require_tld: false })
|
||||||
|
@IsOptional()
|
||||||
|
SERVER_URL: string;
|
||||||
|
|
||||||
// Json Web Token
|
// Json Web Token
|
||||||
@IsString()
|
@IsString()
|
||||||
ACCESS_TOKEN_SECRET: string;
|
ACCESS_TOKEN_SECRET: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user