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

Adding tests for origDatablocks #640

Merged
merged 13 commits into from
Aug 10, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:
ACCESS_GROUPS_STATIC_VALUES: "ess"
CREATE_DATASET_WITH_PID_GROUPS: "group2,group3"
DATASET_CREATION_VALIDATION_ENABLED: true
DATASET_CREATION_VALIDATION_REGEX: "^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
DATASET_CREATION_VALIDATION_REGEX: "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$"

# Start mongo container and app before running api tests
run: |
Expand Down
16 changes: 16 additions & 0 deletions src/common/dto/ownable.dto.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsOptional, IsString } from "class-validator";

export class OwnableDto {
@ApiProperty({
type: String,
required: true,
description: "Name of the group owning this item.",
})
@IsString()
readonly ownerGroup: string;

@ApiProperty({
type: [String],
required: false,
description: "List of groups which have access to this item.",
})
@IsOptional()
@IsString({
each: true,
})
readonly accessGroups?: string[];

@ApiProperty({
type: String,
required: false,
description: "Group of the instrument which this item was acquired on.",
})
@IsOptional()
@IsString()
readonly instrumentGroup?: string;
Expand Down
6 changes: 3 additions & 3 deletions src/datasets/datasets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ export class DatasetsController {
}
case "origdatablocks": {
dataset.origdatablocks = await this.origDatablocksService.findAll(
{ datasetId: dataset.pid },
{ where: { datasetId: dataset.pid } },
);
break;
}
Expand Down Expand Up @@ -1388,7 +1388,7 @@ export class DatasetsController {
): Promise<OrigDatablock[]> {
await this.checkPermissionsForDataset(request, id);

return this.origDatablocksService.findAll({ datasetId: id });
return this.origDatablocksService.findAll({ where: { datasetId: id } });
}

// PATCH /datasets/:id/origdatablocks/:fk
Expand Down Expand Up @@ -1500,7 +1500,7 @@ export class DatasetsController {
});
// all the remaing orig datablocks for this dataset
const odb = await this.origDatablocksService.findAll({
datasetId: datasetId,
where: { datasetId: datasetId },
});
// update dataset size and files number
const updateDatasetDto: PartialUpdateDatasetDto = {
Expand Down
1 change: 1 addition & 0 deletions src/datasets/datasets.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { PoliciesModule } from "src/policies/policies.module";
this.accessGroups,
this.ownerEmail ?? "",
av,
this.createdBy,
);
}
this.classification = `IN=medium,AV=${av},CO=low`;
Expand Down
16 changes: 13 additions & 3 deletions src/origdatablocks/dto/create-origdatablock.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class CreateOrigDatablockDto extends OwnableDto {
type: String,
required: false,
description:
"Name of the hasing algorithm used to compute the hash for each file.",
"Name of the hashing algorithm used to compute the hash for each file.",
})
@IsOptional()
@IsString()
Expand All @@ -46,12 +46,22 @@ export class CreateOrigDatablockDto extends OwnableDto {
type: "array",
items: { $ref: getSchemaPath(DataFile) },
required: true,
description:
"Name of the hasing algorithm used to compute the hash for each file.",
description: "List of the files contained in this orig datablock.",
})
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => DataFileDto)
readonly dataFileList: DataFile[];

@ApiProperty({
type: String,
required: false,
description:
"Name of the group owning this item. If it is not specified, the datasets owner group is used.",
})
@IsOptional()
@IsString()
@IsNotEmpty()
readonly ownerGroup: string;
}
128 changes: 117 additions & 11 deletions src/origdatablocks/origdatablocks.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ApiBearerAuth,
ApiBody,
ApiOperation,
ApiParam,
ApiQuery,
ApiResponse,
ApiTags,
Expand All @@ -38,6 +39,8 @@ import { AllowAny } from "src/auth/decorators/allow-any.decorator";
import { plainToInstance } from "class-transformer";
import { validate, ValidationError } from "class-validator";
import { DatasetsService } from "src/datasets/datasets.service";
import { PartialUpdateDatasetDto } from "src/datasets/dto/update-dataset.dto";
import { filterDescription, filterExample } from "src/common/utils";

@ApiBearerAuth()
@ApiTags("origdatablocks")
Expand All @@ -53,7 +56,7 @@ export class OrigDatablocksController {
@CheckPolicies((ability: AppAbility) =>
ability.can(Action.Create, OrigDatablock),
)
@HttpCode(HttpStatus.OK)
@HttpCode(HttpStatus.CREATED)
@Post()
@ApiOperation({
summary: "It creates a new orig datablock for the specified dataset.",
Expand All @@ -68,7 +71,8 @@ export class OrigDatablocksController {
@ApiResponse({
status: 201,
type: OrigDatablock,
description: "Create a new dataset and return its representation in SciCat",
description:
"Create a new origdataset and return its representation in SciCat",
})
async create(
@Body() createOrigDatablockDto: CreateOrigDatablockDto,
Expand All @@ -79,7 +83,47 @@ export class OrigDatablocksController {
if (!dataset) {
throw new BadRequestException("Invalid datasetId");
}
return this.origDatablocksService.create(createOrigDatablockDto);

createOrigDatablockDto = {
...createOrigDatablockDto,
ownerGroup: createOrigDatablockDto.ownerGroup
? createOrigDatablockDto.ownerGroup
: dataset.ownerGroup,
accessGroups: createOrigDatablockDto.accessGroups
? createOrigDatablockDto.accessGroups
: JSON.parse(JSON.stringify(dataset.accessGroups)),
instrumentGroup: createOrigDatablockDto.instrumentGroup
? createOrigDatablockDto.instrumentGroup
: dataset.instrumentGroup,
};

const origdatablock = await this.origDatablocksService.create(
createOrigDatablockDto,
);

await this.updateDatasetSizeAndFiles(dataset.pid);

return origdatablock;
}

async updateDatasetSizeAndFiles(pid: string) {
// updates datasets size
const parsedFilters: IFilters<OrigDatablockDocument, IOrigDatablockFields> =
{ where: { datasetId: pid } };
const datasetOrigdatablocks = await this.origDatablocksService.findAll(
parsedFilters,
);

const updateDatasetDto: PartialUpdateDatasetDto = {
size: datasetOrigdatablocks
.map((od) => od.size)
.reduce((ps, a) => ps + a, 0),
numberOfFiles: datasetOrigdatablocks
.map((od) => od.dataFileList.length)
.reduce((ps, a) => ps + a, 0),
};

await this.datasetsService.findByIdAndUpdate(pid, updateDatasetDto);
}

@AllowAny()
Expand Down Expand Up @@ -129,19 +173,23 @@ export class OrigDatablocksController {
"It returns a list of orig datablocks. The list returned can be modified by providing a filter.",
})
@ApiQuery({
name: "filters",
description: "Database filters to apply when retrieving all origdatablocks",
name: "filter",
description:
"Database filters to apply when retrieving all origdatablocks\n" +
filterDescription,
required: false,
type: String,
example: filterExample,
})
@ApiResponse({
status: 200,
type: OrigDatablock,
isArray: true,
description: "Return the orig datablocks requested",
})
async findAll(@Query("filters") filters?: string): Promise<OrigDatablock[]> {
async findAll(@Query("filter") filter?: string): Promise<OrigDatablock[]> {
const parsedFilters: IFilters<OrigDatablockDocument, IOrigDatablockFields> =
JSON.parse(filters ?? "{}");
JSON.parse(filter ?? "{}");
return this.origDatablocksService.findAll(parsedFilters);
}

Expand Down Expand Up @@ -252,6 +300,20 @@ export class OrigDatablocksController {
ability.can(Action.Read, OrigDatablock),
)
@Get("/:id")
@ApiOperation({
summary: "It retrieve the origdatablock.",
description: "It retrieve the original datablock with the id specified.",
})
@ApiParam({
name: "id",
description: "Id of the origdatablock to be retrieved",
type: String,
})
@ApiResponse({
status: 200,
description: "The origdatablock requested",
type: OrigDatablock,
})
async findById(@Param("id") id: string): Promise<OrigDatablock | null> {
return this.origDatablocksService.findOne({ _id: id });
}
Expand All @@ -262,23 +324,67 @@ export class OrigDatablocksController {
ability.can(Action.Update, OrigDatablock),
)
@Patch("/:id")
@ApiOperation({
summary: "It updates the origdatablock.",
description: "It updates the original datablock with the id specified.",
})
@ApiParam({
name: "id",
description: "Id of the origdatablock to be updated",
type: String,
})
@ApiBody({
description:
"OrigDatablock object that needs to be updated. Only the origdatablock object fields that needs to be updated, should be passed in.",
required: true,
type: UpdateOrigDatablockDto,
})
@ApiResponse({
status: 200,
description: "The updated origdatablock",
type: OrigDatablock,
})
async update(
@Param("id") id: string,
@Body() updateOrigDatablockDto: UpdateOrigDatablockDto,
): Promise<OrigDatablock | null> {
return this.origDatablocksService.update(
const origdatablock = (await this.origDatablocksService.update(
{ _id: id },
updateOrigDatablockDto,
);
)) as OrigDatablock;

await this.updateDatasetSizeAndFiles(origdatablock.datasetId);

return origdatablock;
}

// DELETE /origdatablocks/:id
@UseGuards()
@UseGuards(PoliciesGuard)
@CheckPolicies((ability: AppAbility) =>
ability.can(Action.Delete, OrigDatablock),
)
@Delete("/:id")
@ApiOperation({
summary: "It deletes the origdatablock.",
description:
"It delete the original datablock specified through the id specified.",
})
@ApiParam({
name: "id",
description: "Id of the origdatablock to be deleted",
type: String,
})
@ApiResponse({
status: 200,
description: "No value is returned",
})
async remove(@Param("id") id: string): Promise<unknown> {
return this.origDatablocksService.remove({ _id: id });
const origdatablock = (await this.origDatablocksService.remove({
_id: id,
})) as OrigDatablock;

await this.updateDatasetSizeAndFiles(origdatablock.datasetId);

return origdatablock;
}
}
15 changes: 14 additions & 1 deletion src/origdatablocks/origdatablocks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,20 @@ export class OrigDatablocksService {
async findAll(
filter: FilterQuery<OrigDatablockDocument>,
): Promise<OrigDatablock[]> {
return this.origDatablockModel.find(filter).exec();
const whereFilter: FilterQuery<OrigDatablockDocument> = filter.where ?? {};
const fieldsProjection: FilterQuery<OrigDatablockDocument> =
filter.fields ?? {};
const { limit, skip, sort } = parseLimitFilters(filter.limits);

const origdatablockPromise = this.origDatablockModel
.find(whereFilter, fieldsProjection)
.limit(limit)
.skip(skip)
.sort(sort);

const origdatablock = await origdatablockPromise.exec();

return origdatablock;
}

async findOne(
Expand Down
Loading