2021-09-13 17:24:47 +00:00
|
|
|
import {
|
|
|
|
|
Controller,
|
|
|
|
|
Get,
|
|
|
|
|
Param,
|
2022-04-20 09:16:57 +00:00
|
|
|
Body,
|
2021-09-13 17:24:47 +00:00
|
|
|
Post,
|
|
|
|
|
Patch,
|
|
|
|
|
Delete,
|
|
|
|
|
Query,
|
|
|
|
|
UseGuards,
|
|
|
|
|
ForbiddenException,
|
2022-12-09 14:53:42 +00:00
|
|
|
BadRequestException,
|
2023-03-24 16:11:21 +00:00
|
|
|
Put,
|
2021-09-13 17:24:47 +00:00
|
|
|
} from '@nestjs/common';
|
2021-07-14 14:14:35 +00:00
|
|
|
import { JwtAuthGuard } from '../../src/modules/auth/jwt-auth.guard';
|
2021-07-11 08:32:06 +00:00
|
|
|
import { decamelizeKeys } from 'humps';
|
2021-07-14 14:14:35 +00:00
|
|
|
import { DataQueriesService } from '../../src/services/data_queries.service';
|
2021-07-16 15:03:20 +00:00
|
|
|
import { DataSourcesService } from '../../src/services/data_sources.service';
|
2021-07-24 06:13:21 +00:00
|
|
|
import { QueryAuthGuard } from 'src/modules/auth/query-auth.guard';
|
|
|
|
|
import { AppsAbilityFactory } from 'src/modules/casl/abilities/apps-ability.factory';
|
2021-07-24 18:09:25 +00:00
|
|
|
import { AppsService } from '@services/apps.service';
|
2022-04-20 09:16:57 +00:00
|
|
|
import { CreateDataQueryDto, UpdateDataQueryDto } from '@dto/data-query.dto';
|
2022-05-05 07:08:42 +00:00
|
|
|
import { User } from 'src/decorators/user.decorator';
|
2022-10-27 11:29:43 +00:00
|
|
|
import { decode } from 'js-base64';
|
2022-12-09 14:53:42 +00:00
|
|
|
import { dbTransactionWrap } from 'src/helpers/utils.helper';
|
|
|
|
|
import { EntityManager } from 'typeorm';
|
|
|
|
|
import { DataSource } from 'src/entities/data_source.entity';
|
2023-03-24 16:11:21 +00:00
|
|
|
import { DataSourceScopes, DataSourceTypes } from 'src/helpers/data_source.constants';
|
|
|
|
|
import { App } from 'src/entities/app.entity';
|
2023-08-09 12:31:48 +00:00
|
|
|
import { isEmpty } from 'class-validator';
|
2021-07-11 08:32:06 +00:00
|
|
|
|
|
|
|
|
@Controller('data_queries')
|
|
|
|
|
export class DataQueriesController {
|
|
|
|
|
constructor(
|
2021-07-24 18:09:25 +00:00
|
|
|
private appsService: AppsService,
|
2021-07-16 15:03:20 +00:00
|
|
|
private dataQueriesService: DataQueriesService,
|
2021-07-24 06:13:21 +00:00
|
|
|
private dataSourcesService: DataSourcesService,
|
2021-09-21 13:48:28 +00:00
|
|
|
private appsAbilityFactory: AppsAbilityFactory
|
2021-12-10 03:13:05 +00:00
|
|
|
) {}
|
2021-07-11 08:32:06 +00:00
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@Get()
|
2022-05-05 07:08:42 +00:00
|
|
|
async index(@User() user, @Query() query) {
|
2022-12-09 14:53:42 +00:00
|
|
|
const app = await this.appsService.findAppFromVersion(query.app_version_id);
|
|
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
2021-07-24 18:09:25 +00:00
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
if (!ability.can('getQueries', app)) {
|
2021-07-24 18:09:25 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
|
|
|
|
}
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
const queries = await this.dataQueriesService.all(query);
|
2021-08-10 08:48:20 +00:00
|
|
|
const seralizedQueries = [];
|
|
|
|
|
|
|
|
|
|
// serialize
|
2021-09-21 13:48:28 +00:00
|
|
|
for (const query of queries) {
|
2022-12-27 10:46:13 +00:00
|
|
|
if (query.dataSource.type === DataSourceTypes.STATIC) {
|
2022-12-09 14:53:42 +00:00
|
|
|
delete query['dataSourceId'];
|
|
|
|
|
}
|
|
|
|
|
delete query['dataSource'];
|
|
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
const decamelizedQuery = decamelizeKeys(query);
|
2021-08-10 08:48:20 +00:00
|
|
|
|
|
|
|
|
decamelizedQuery['options'] = query.options;
|
2022-10-27 11:29:43 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
if (query.plugin) {
|
2022-10-27 11:29:43 +00:00
|
|
|
decamelizedQuery['plugin'].manifest_file.data = JSON.parse(
|
|
|
|
|
decode(query.plugin.manifestFile.data.toString('utf8'))
|
|
|
|
|
);
|
|
|
|
|
decamelizedQuery['plugin'].icon_file.data = query.plugin.iconFile.data.toString('utf8');
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-10 08:48:20 +00:00
|
|
|
seralizedQueries.push(decamelizedQuery);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = { data_queries: seralizedQueries };
|
2021-07-11 08:32:06 +00:00
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-12 14:12:34 +00:00
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@Post()
|
2022-05-05 07:08:42 +00:00
|
|
|
async create(@User() user, @Body() dataQueryDto: CreateDataQueryDto): Promise<object> {
|
2022-12-09 14:53:42 +00:00
|
|
|
const {
|
|
|
|
|
kind,
|
|
|
|
|
name,
|
|
|
|
|
options,
|
|
|
|
|
data_source_id: dataSourceId,
|
|
|
|
|
plugin_id: pluginId,
|
|
|
|
|
app_version_id: appVersionId,
|
|
|
|
|
} = dataQueryDto;
|
2021-07-24 18:09:25 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
let dataSource: DataSource;
|
2023-03-24 16:11:21 +00:00
|
|
|
let app: App;
|
2021-07-24 18:09:25 +00:00
|
|
|
|
2022-12-28 14:36:55 +00:00
|
|
|
if (!dataSourceId && !(kind === 'restapi' || kind === 'runjs' || kind === 'tooljetdb' || kind === 'runpy')) {
|
2022-12-09 14:53:42 +00:00
|
|
|
throw new BadRequestException();
|
2021-07-24 18:09:25 +00:00
|
|
|
}
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
return dbTransactionWrap(async (manager: EntityManager) => {
|
2022-12-28 14:36:55 +00:00
|
|
|
if (!dataSourceId && (kind === 'restapi' || kind === 'runjs' || kind === 'tooljetdb' || kind === 'runpy')) {
|
2023-03-24 16:11:21 +00:00
|
|
|
dataSource = await this.dataSourcesService.findDefaultDataSource(
|
|
|
|
|
kind,
|
|
|
|
|
appVersionId,
|
|
|
|
|
pluginId,
|
|
|
|
|
user.organizationId,
|
|
|
|
|
manager
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
dataSource = await this.dataSourcesService.findOne(dataSource?.id || dataSourceId, manager);
|
|
|
|
|
|
|
|
|
|
if (dataSource.scope === DataSourceScopes.GLOBAL) {
|
|
|
|
|
app = await this.appsService.findAppFromVersion(appVersionId);
|
|
|
|
|
} else {
|
|
|
|
|
app = await this.dataSourcesService.findApp(dataSource?.id || dataSourceId, manager);
|
2022-12-09 14:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
|
|
|
|
|
|
|
|
|
if (!ability.can('createQuery', app)) {
|
2021-07-24 18:09:25 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
|
|
|
|
}
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
// todo: pass the whole dto instead of indv. values
|
2023-03-24 16:11:21 +00:00
|
|
|
const dataQuery = await this.dataQueriesService.create(
|
|
|
|
|
name,
|
|
|
|
|
options,
|
|
|
|
|
dataSource?.id || dataSourceId,
|
|
|
|
|
appVersionId,
|
|
|
|
|
manager
|
|
|
|
|
);
|
2023-08-09 12:31:48 +00:00
|
|
|
|
|
|
|
|
const decamelizedQuery = decamelizeKeys({ ...dataQuery, kind });
|
|
|
|
|
|
|
|
|
|
decamelizedQuery['options'] = dataQuery.options;
|
|
|
|
|
|
|
|
|
|
return decamelizedQuery;
|
2022-12-09 14:53:42 +00:00
|
|
|
});
|
2021-07-12 14:12:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
2021-07-24 18:09:25 +00:00
|
|
|
@Patch(':id')
|
2022-12-09 14:53:42 +00:00
|
|
|
async update(@User() user, @Param('id') dataQueryId, @Body() updateDataQueryDto: UpdateDataQueryDto) {
|
2022-04-20 09:16:57 +00:00
|
|
|
const { name, options } = updateDataQueryDto;
|
2021-07-24 18:09:25 +00:00
|
|
|
|
|
|
|
|
const dataQuery = await this.dataQueriesService.findOne(dataQueryId);
|
2023-03-24 16:11:21 +00:00
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, dataQuery.app.id);
|
2021-07-24 18:09:25 +00:00
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
if (!ability.can('updateQuery', dataQuery.app)) {
|
2021-07-24 18:09:25 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
|
|
|
|
}
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
const result = await this.dataQueriesService.update(dataQueryId, name, options);
|
2023-08-09 12:31:48 +00:00
|
|
|
const decamelizedQuery = decamelizeKeys({ ...dataQuery, ...result });
|
|
|
|
|
decamelizedQuery['options'] = result.options;
|
|
|
|
|
return decamelizedQuery;
|
2021-07-12 14:12:34 +00:00
|
|
|
}
|
|
|
|
|
|
2021-09-13 17:24:47 +00:00
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@Delete(':id')
|
2022-12-09 14:53:42 +00:00
|
|
|
async delete(@User() user, @Param('id') dataQueryId) {
|
2021-09-13 17:24:47 +00:00
|
|
|
const dataQuery = await this.dataQueriesService.findOne(dataQueryId);
|
2023-03-24 16:11:21 +00:00
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, dataQuery.app.id);
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
if (!ability.can('deleteQuery', dataQuery.app)) {
|
2021-09-21 13:48:28 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
2021-09-13 17:24:47 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
const result = await this.dataQueriesService.delete(dataQueryId);
|
2021-09-13 17:24:47 +00:00
|
|
|
return decamelizeKeys(result);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-24 06:13:21 +00:00
|
|
|
@UseGuards(QueryAuthGuard)
|
2022-12-09 14:53:42 +00:00
|
|
|
@Post([':id/run/:environmentId', ':id/run'])
|
|
|
|
|
async runQuery(
|
|
|
|
|
@User() user,
|
|
|
|
|
@Param('id') dataQueryId,
|
|
|
|
|
@Param('environmentId') environmentId,
|
|
|
|
|
@Body() updateDataQueryDto: UpdateDataQueryDto
|
|
|
|
|
) {
|
2023-08-09 12:31:48 +00:00
|
|
|
const { options, resolvedOptions } = updateDataQueryDto;
|
2021-07-14 14:14:35 +00:00
|
|
|
|
2021-07-16 15:03:20 +00:00
|
|
|
const dataQuery = await this.dataQueriesService.findOne(dataQueryId);
|
|
|
|
|
|
2022-05-05 07:08:42 +00:00
|
|
|
if (user) {
|
2023-03-24 16:11:21 +00:00
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, dataQuery.app.id);
|
2021-07-24 06:13:21 +00:00
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
if (!ability.can('runQuery', dataQuery.app)) {
|
2021-07-24 06:13:21 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
|
|
|
|
}
|
2023-08-09 12:31:48 +00:00
|
|
|
|
|
|
|
|
if (ability.can('updateQuery', dataQuery.app) && !isEmpty(options)) {
|
|
|
|
|
await this.dataQueriesService.update(dataQueryId, dataQuery.name, options);
|
|
|
|
|
dataQuery['options'] = options;
|
|
|
|
|
}
|
2021-07-24 06:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-17 07:38:02 +00:00
|
|
|
let result = {};
|
|
|
|
|
|
|
|
|
|
try {
|
2023-08-09 12:31:48 +00:00
|
|
|
result = await this.dataQueriesService.runQuery(user, dataQuery, resolvedOptions, environmentId);
|
2021-07-17 07:38:02 +00:00
|
|
|
} catch (error) {
|
2022-02-05 01:39:40 +00:00
|
|
|
if (error.constructor.name === 'QueryError') {
|
2021-07-17 07:38:02 +00:00
|
|
|
result = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: error.message,
|
|
|
|
|
description: error.description,
|
2021-09-21 13:48:28 +00:00
|
|
|
data: error.data,
|
|
|
|
|
};
|
2021-07-17 07:38:02 +00:00
|
|
|
} else {
|
2021-07-17 14:22:37 +00:00
|
|
|
console.log(error);
|
2021-07-17 07:38:02 +00:00
|
|
|
result = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: 'Internal server error',
|
|
|
|
|
description: error.message,
|
2021-09-21 13:48:28 +00:00
|
|
|
data: {},
|
|
|
|
|
};
|
2021-07-17 07:38:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-16 15:03:20 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
2022-12-09 14:53:42 +00:00
|
|
|
@Post(['/preview/:environmentId', '/preview'])
|
|
|
|
|
async previewQuery(
|
|
|
|
|
@User() user,
|
|
|
|
|
@Body() updateDataQueryDto: UpdateDataQueryDto,
|
|
|
|
|
@Param('environmentId') environmentId
|
|
|
|
|
) {
|
2022-12-15 10:31:10 +00:00
|
|
|
const { options, query, app_version_id: appVersionId } = updateDataQueryDto;
|
|
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
const app = await this.appsService.findAppFromVersion(appVersionId);
|
|
|
|
|
|
2022-12-15 11:06:25 +00:00
|
|
|
if (!(query['data_source_id'] || appVersionId || environmentId)) {
|
|
|
|
|
throw new BadRequestException('Data source id or app version id or environment id is mandatory');
|
2022-12-15 10:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-09 14:53:42 +00:00
|
|
|
const kind = query ? query['kind'] : null;
|
2021-07-16 15:03:20 +00:00
|
|
|
const dataQueryEntity = {
|
|
|
|
|
...query,
|
2023-03-24 16:11:21 +00:00
|
|
|
app,
|
2022-12-09 14:53:42 +00:00
|
|
|
dataSource: query['data_source_id']
|
|
|
|
|
? await this.dataSourcesService.findOne(query['data_source_id'])
|
2023-03-24 16:11:21 +00:00
|
|
|
: await this.dataSourcesService.findDefaultDataSourceByKind(kind, appVersionId),
|
2021-09-21 13:48:28 +00:00
|
|
|
};
|
2021-07-16 15:03:20 +00:00
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
2021-07-24 18:09:25 +00:00
|
|
|
|
2023-03-24 16:11:21 +00:00
|
|
|
if (!ability.can('previewQuery', app)) {
|
2022-12-09 14:53:42 +00:00
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
2021-07-24 18:09:25 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-17 04:41:02 +00:00
|
|
|
let result = {};
|
|
|
|
|
|
|
|
|
|
try {
|
2022-12-09 14:53:42 +00:00
|
|
|
result = await this.dataQueriesService.runQuery(user, dataQueryEntity, options, environmentId);
|
2021-07-17 04:41:02 +00:00
|
|
|
} catch (error) {
|
2022-02-05 01:39:40 +00:00
|
|
|
if (error.constructor.name === 'QueryError') {
|
2021-07-17 04:41:02 +00:00
|
|
|
result = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: error.message,
|
|
|
|
|
description: error.description,
|
2021-09-21 13:48:28 +00:00
|
|
|
data: error.data,
|
|
|
|
|
};
|
2021-07-17 04:41:02 +00:00
|
|
|
} else {
|
2021-07-17 14:22:37 +00:00
|
|
|
console.log(error);
|
2021-07-17 04:41:02 +00:00
|
|
|
result = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: 'Internal server error',
|
|
|
|
|
description: error.message,
|
2021-09-21 13:48:28 +00:00
|
|
|
data: {},
|
|
|
|
|
};
|
2021-07-17 04:41:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-09-13 17:24:47 +00:00
|
|
|
|
2021-07-16 12:37:30 +00:00
|
|
|
return result;
|
2021-07-14 14:14:35 +00:00
|
|
|
}
|
2023-03-24 16:11:21 +00:00
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@Put(':id/data_source')
|
|
|
|
|
async changeQueryDataSource(@User() user, @Param('id') queryId, @Body() updateDataQueryDto: UpdateDataQueryDto) {
|
|
|
|
|
const { data_source_id: dataSourceId } = updateDataQueryDto;
|
|
|
|
|
|
|
|
|
|
const dataQuery = await this.dataQueriesService.findOne(queryId);
|
|
|
|
|
const ability = await this.appsAbilityFactory.appsActions(user, dataQuery.app.id);
|
|
|
|
|
|
|
|
|
|
if (!ability.can('updateQuery', dataQuery.app)) {
|
|
|
|
|
throw new ForbiddenException('you do not have permissions to perform this action');
|
|
|
|
|
}
|
|
|
|
|
await this.dataQueriesService.changeQueryDataSource(queryId, dataSourceId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-07-11 08:32:06 +00:00
|
|
|
}
|