Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/change tools #3430

Merged
merged 12 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions api-gateway/src/api/service/artifact.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { UserRole } from '@guardian/interfaces';
import { Logger } from '@guardian/common';
import { Guardians } from '@helpers/guardians';
import { Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Post, Req, Response } from '@nestjs/common';
import {
Controller,
Delete,
Get,
HttpCode,
HttpException,
HttpStatus,
Post,
Req,
Response,
UploadedFiles,
UseInterceptors,
} from '@nestjs/common';
import { checkPermission } from '@auth/authorization-helper';
import {
ApiExtraModels,
Expand All @@ -12,12 +24,15 @@ import {
ApiTags,
ApiUnauthorizedResponse,
ApiForbiddenResponse,
getSchemaPath
getSchemaPath,
ApiBody,
ApiConsumes
} from '@nestjs/swagger';
import { InternalServerErrorDTO } from '@middlewares/validation/schemas/errors';
import { ApiImplicitQuery } from '@nestjs/swagger/dist/decorators/api-implicit-query.decorator';
import { ArtifactDTOItem } from '@middlewares/validation/schemas/artifacts';
import { ApiImplicitParam } from '@nestjs/swagger/dist/decorators/api-implicit-param.decorator';
import { FilesInterceptor } from '@nestjs/platform-express';

@Controller('artifacts')
@ApiTags('artifacts')
Expand Down Expand Up @@ -127,6 +142,23 @@ export class ArtifactApi {
required: true,
example: '000000000000000000000001'
})
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Form data with artifacts.',
required: true,
schema: {
type: 'array',
items: {
type: 'object',
properties: {
'artifacts': {
type: 'string',
format: 'binary',
}
}
}
}
})
@ApiOkResponse({
description: 'Successful operation.',
schema: {
Expand All @@ -149,26 +181,25 @@ export class ArtifactApi {
}
})
@ApiExtraModels(ArtifactDTOItem, InternalServerErrorDTO)
@UseInterceptors(FilesInterceptor('artifacts'))
@HttpCode(HttpStatus.CREATED)
async uploadArtifacts(@Req() req, @Response() res): Promise<any> {
async uploadArtifacts(@Req() req, @UploadedFiles() files): Promise<any> {
await checkPermission(UserRole.STANDARD_REGISTRY)(req.user);
try {
const files = req.files;
if (!files) {
throw new HttpException('There are no files to upload', HttpStatus.UNPROCESSABLE_ENTITY)
}
const owner = req.user.did;
const parentId = req.params.parentId;
const uploadedArtifacts = [];
const artifacts = Array.isArray(files.artifacts) ? files.artifacts : [files.artifacts];
const guardian = new Guardians();
for (const artifact of artifacts) {
for (const artifact of files) {
if (artifact) {
const result = await guardian.uploadArtifact(artifact, owner, parentId);
uploadedArtifacts.push(result);
}
}
return res.status(201).json(uploadedArtifacts);
return uploadedArtifacts;
} catch (error) {
new Logger().error(error, ['API_GATEWAY']);
throw error;
Expand Down
261 changes: 248 additions & 13 deletions api-gateway/src/api/service/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,44 @@ import { ServiceError } from '@helpers/service-requests-base';
import { TaskManager } from '@helpers/task-manager';
import { Users } from '@helpers/users';
import { InternalServerErrorDTO } from '@middlewares/validation/schemas/errors';
import { MigrationConfigDTO, PolicyCategoryDTO } from '@middlewares/validation/schemas/policies';
import { Body, Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Param, Post, Put, Query, Req, Response } from '@nestjs/common';
import { ApiAcceptedResponse, ApiBody, ApiExtraModels, ApiForbiddenResponse, ApiInternalServerErrorResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiSecurity, ApiTags, ApiUnauthorizedResponse, getSchemaPath } from '@nestjs/swagger';
import {
MigrationConfigDTO,
PolicyCategoryDTO,
} from '@middlewares/validation/schemas/policies';
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpException,
HttpStatus,
Param,
Post,
Put,
Query,
Req,
Response,
UploadedFiles,
UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import {
ApiAcceptedResponse,
ApiBody,
ApiConsumes,
ApiExtraModels,
ApiForbiddenResponse,
ApiInternalServerErrorResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiQuery,
ApiSecurity,
ApiTags,
ApiUnauthorizedResponse,
getSchemaPath,
} from '@nestjs/swagger';
import { ApiImplicitParam } from '@nestjs/swagger/dist/decorators/api-implicit-param.decorator';
import { ApiImplicitQuery } from '@nestjs/swagger/dist/decorators/api-implicit-query.decorator';

Expand Down Expand Up @@ -1178,11 +1213,19 @@ export class PolicyApi {
const engineService = new PolicyEngine();
const versionOfTopicId = req.query ? req.query.versionOfTopicId : null;
try {
const policies = await engineService.importMessage(req.user, req.body.messageId, versionOfTopicId);
const policies = await engineService.importMessage(
req.user,
req.body.messageId,
versionOfTopicId,
req.body.metadata
);
return res.status(201).send(policies);
} catch (error) {
new Logger().error(error, ['API_GATEWAY']);
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
throw new HttpException(
error.message,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

Expand Down Expand Up @@ -1212,14 +1255,29 @@ export class PolicyApi {
const messageId = req.body.messageId;
const versionOfTopicId = req.query ? req.query.versionOfTopicId : null;
const taskManager = new TaskManager();
const task = taskManager.start(TaskAction.IMPORT_POLICY_MESSAGE, user.id);
RunFunctionAsync<ServiceError>(async () => {
const engineService = new PolicyEngine();
await engineService.importMessageAsync(user, messageId, versionOfTopicId, task);
}, async (error) => {
new Logger().error(error, ['API_GATEWAY']);
taskManager.addError(task.taskId, { code: 500, message: 'Unknown error: ' + error.message });
});
const task = taskManager.start(
TaskAction.IMPORT_POLICY_MESSAGE,
user.id
);
RunFunctionAsync<ServiceError>(
async () => {
const engineService = new PolicyEngine();
await engineService.importMessageAsync(
user,
messageId,
versionOfTopicId,
task,
req.body.metadata
);
},
async (error) => {
new Logger().error(error, ['API_GATEWAY']);
taskManager.addError(task.taskId, {
code: 500,
message: 'Unknown error: ' + error.message,
});
}
);
return res.status(202).send(task);
}

Expand Down Expand Up @@ -1347,6 +1405,91 @@ export class PolicyApi {
}
}

/**
* Policy import from a zip file with metadata.
*/
@Post('/import/file-metadata')
@Auth(
UserRole.STANDARD_REGISTRY
)
@ApiSecurity('bearerAuth')
@ApiOperation({
summary: 'Imports new policy from a zip file with metadata.',
description: 'Imports new policy and all associated artifacts, such as schemas and VCs, from the provided zip file into the local DB.' + ONLY_SR,
})
@ApiImplicitQuery({
name: 'versionOfTopicId',
type: String,
description: 'Topic Id',
required: false
})
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Form data with policy file and metadata.',
required: true,
schema: {
type: 'object',
properties: {
'policyFile': {
type: 'string',
format: 'binary',
},
'metadata': {
type: 'string',
format: 'binary',
}
}
}
})
@ApiOkResponse({
description: 'Successful operation.',
schema: {
'type': 'object'
},
})
@ApiUnauthorizedResponse({
description: 'Unauthorized.',
})
@ApiForbiddenResponse({
description: 'Forbidden.',
})
@ApiInternalServerErrorResponse({
description: 'Internal server error.',
type: InternalServerErrorDTO
})
@HttpCode(HttpStatus.CREATED)
@UseInterceptors(AnyFilesInterceptor())
async importPolicyFromFileWithMetadata(
@AuthUser() user: IAuthUser,
@UploadedFiles() files: any,
@Query('versionOfTopicId') versionOfTopicId,
): Promise<any> {
try {
const policyFile = files.find(
(item) => item.fieldname === 'policyFile'
);
if (!policyFile) {
throw new Error('There is no policy file');
}
const metadata = files.find(
(item) => item.fieldname === 'metadata'
);
const engineService = new PolicyEngine();
return await engineService.importFile(
user,
policyFile.buffer,
versionOfTopicId,
metadata?.buffer && JSON.parse(metadata.buffer.toString())
);
} catch (error) {
new Logger().error(error, ['API_GATEWAY']);
throw new HttpException(
error.message,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

/**
* Policy import from a zip file (async).
*/
Expand Down Expand Up @@ -1405,6 +1548,98 @@ export class PolicyApi {
return res.status(202).send(task);
}

/**
* Policy import from a zip file with metadata (async).
*/
@Post('/push/import/file-metadata')
@Auth(
UserRole.STANDARD_REGISTRY
)
@ApiSecurity('bearerAuth')
@ApiOperation({
summary: 'Imports new policy from a zip file with metadata.',
description: 'Imports new policy and all associated artifacts, such as schemas and VCs, from the provided zip file into the local DB.' + ONLY_SR,
})
@ApiImplicitQuery({
name: 'versionOfTopicId',
type: String,
description: 'Topic Id',
required: false
})
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Form data with policy file and metadata.',
required: true,
schema: {
type: 'object',
properties: {
'policyFile': {
type: 'string',
format: 'binary',
},
'metadata': {
type: 'string',
format: 'binary',
}
}
}
})
@ApiOkResponse({
description: 'Successful operation.',
schema: {
'type': 'object'
},
})
@ApiUnauthorizedResponse({
description: 'Unauthorized.',
})
@ApiForbiddenResponse({
description: 'Forbidden.',
})
@ApiInternalServerErrorResponse({
description: 'Internal server error.',
type: InternalServerErrorDTO
})
@HttpCode(HttpStatus.ACCEPTED)
@UseInterceptors(AnyFilesInterceptor())
async importPolicyFromFileWithMetadataAsync(
@AuthUser() user: IAuthUser,
@UploadedFiles() files: any,
@Query('versionOfTopicId') versionOfTopicId,
): Promise<any> {
const taskManager = new TaskManager();
const task = taskManager.start(TaskAction.IMPORT_POLICY_FILE, user.id);
RunFunctionAsync<ServiceError>(
async () => {
const policyFile = files.find(
(item) => item.fieldname === 'policyFile'
);
if (!policyFile) {
throw new Error('There is no policy file');
}
const metadata = files.find(
(item) => item.fieldname === 'metadata'
);
const engineService = new PolicyEngine();
await engineService.importFileAsync(
user,
policyFile.buffer,
versionOfTopicId,
task,
metadata?.buffer && JSON.parse(metadata.buffer.toString())
);
},
async (error) => {
new Logger().error(error, ['API_GATEWAY']);
taskManager.addError(task.taskId, {
code: 500,
message: 'Unknown error: ' + error.message,
});
}
);
return task;
}

/**
* Policy preview from a zip file.
*/
Expand Down
Loading