From 48d5bc54a35a40a31b295e840a3a11e31282c1c1 Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 11:34:48 -0400 Subject: [PATCH 1/7] Begin work on creating webhook endpoint --- .../download-request/download-request.module.ts | 4 +++- .../pipes/dataset-download-request.pipe.ts | 0 .../pipes/study-download-request.pipe.ts | 17 +++++++++++++++++ .../study-download-request.resolver.ts | 6 ++++++ .../services/study-download-request.service.ts | 4 ++++ 5 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 packages/server/src/download-request/pipes/dataset-download-request.pipe.ts create mode 100644 packages/server/src/download-request/pipes/study-download-request.pipe.ts diff --git a/packages/server/src/download-request/download-request.module.ts b/packages/server/src/download-request/download-request.module.ts index acfe5d38..422863b5 100644 --- a/packages/server/src/download-request/download-request.module.ts +++ b/packages/server/src/download-request/download-request.module.ts @@ -16,6 +16,7 @@ import { StudyModule } from 'src/study/study.module'; import { StudyDownloadRequestResolver } from './resolvers/study-download-request.resolver'; import { StudyDownloadService } from './services/study-download-request.service'; import { TagModule } from '../tag/tag.module'; +import { StudyDownloadRequestPipe } from './pipes/study-download-request.pipe'; @Module({ imports: [ @@ -38,7 +39,8 @@ import { TagModule } from '../tag/tag.module'; CreateDatasetDownloadPipe, CreateStudyDownloadPipe, StudyDownloadRequestResolver, - StudyDownloadService + StudyDownloadService, + StudyDownloadRequestPipe ] }) export class DownloadRequestModule {} diff --git a/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts b/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/server/src/download-request/pipes/study-download-request.pipe.ts b/packages/server/src/download-request/pipes/study-download-request.pipe.ts new file mode 100644 index 00000000..b1953d03 --- /dev/null +++ b/packages/server/src/download-request/pipes/study-download-request.pipe.ts @@ -0,0 +1,17 @@ +import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; +import { StudyDownloadRequest } from '../models/study-download-request.model'; +import { StudyDownloadService } from '../services/study-download-request.service'; + + +@Injectable() +export class StudyDownloadRequestPipe implements PipeTransform> { + constructor(private readonly downloadService: StudyDownloadService) {} + + async transform(value: string): Promise { + const downloadRequest = await this.downloadService.find(value); + if (!downloadRequest) { + throw new BadRequestException(`Study down with the id ${value} not found`); + } + return downloadRequest; + } +} diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index c2aa363f..78bfe9d9 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -29,6 +29,12 @@ export class StudyDownloadRequestResolver { return this.studyDownloadService.getStudyDownloads(study); } + @Mutation(() => Boolean) + async markStudyFieldComplete(@Args('downloadRequest', { type: () => ID }) downloadRequest: StudyDownloadRequest): Promise { + + return true; + } + @ResolveField(() => String) async entryZip(@Parent() downloadRequest: StudyDownloadRequest): Promise { return this.studyDownloadService.getEntryZipUrl(downloadRequest); diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 0e5c9fb7..754c550e 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -102,6 +102,10 @@ export class StudyDownloadService { return this.downloadRequestModel.find({ study: study._id }); } + async find(id: string): Promise { + return this.downloadRequestModel.findById(id); + } + /** * Handles generating the CSV for the tag data. This approach is a sub-optimal one. * From eb6f18b4aac4b30787246dd86b32cca5d9767d53 Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 11:41:14 -0400 Subject: [PATCH 2/7] Add in flag fields for the download completion --- .../models/study-download-request.model.ts | 17 ++++++++++++++++- .../study-download-request.resolver.ts | 2 +- .../services/study-download-request.service.ts | 8 +++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/server/src/download-request/models/study-download-request.model.ts b/packages/server/src/download-request/models/study-download-request.model.ts index b0aa0daa..b66413d3 100644 --- a/packages/server/src/download-request/models/study-download-request.model.ts +++ b/packages/server/src/download-request/models/study-download-request.model.ts @@ -1,7 +1,16 @@ import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; import { DownloadRequest, DownloadStatus } from './download-request.model'; -import { Field, ObjectType } from '@nestjs/graphql'; +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +export enum StudyDownloadField { + ENTRY_ZIP = 'ENTRY_ZIP', + TAGGED_ENTRIES_ZIP = 'TAGGED_ENTRIES_ZIP' +} + +registerEnumType(StudyDownloadField, { + name: 'StudyDownloadField' +}); @Schema() @ObjectType() @@ -54,6 +63,12 @@ export class StudyDownloadRequest implements DownloadRequest { /** Webhook payload to be used when the zipping of tagged entries is complete */ @Prop({ required: false }) taggedEntryWebhookPayloadLocation?: string; + + @Prop({ required: true }) + entryZipComplete: boolean; + + @Prop({ required: true }) + taggedEntryZipComplete: boolean; } export type StudyDownloadRequestDocument = Document & StudyDownloadRequest; diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index 78bfe9d9..fe70ed75 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -31,7 +31,7 @@ export class StudyDownloadRequestResolver { @Mutation(() => Boolean) async markStudyFieldComplete(@Args('downloadRequest', { type: () => ID }) downloadRequest: StudyDownloadRequest): Promise { - + await this.studyDownloadService.markStudyFieldComplete(downloadRequest); return true; } diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 754c550e..563d7c9a 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -39,7 +39,9 @@ export class StudyDownloadService { ...downloadRequest, date: new Date(), status: DownloadStatus.IN_PROGRESS, - organization: organization._id + organization: organization._id, + entryZipComplete: false, + taggedEntryZipComplete: false }); const bucketLocation = `${this.downloadService.getPrefix()}/${request._id}`; @@ -106,6 +108,10 @@ export class StudyDownloadService { return this.downloadRequestModel.findById(id); } + async markStudyFieldComplete(downloadRequest: StudyDownloadRequest): Promise { + + } + /** * Handles generating the CSV for the tag data. This approach is a sub-optimal one. * From 669fb75a224c002662b1328ad425a714494dd768 Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 14:59:47 -0400 Subject: [PATCH 3/7] Add logic for marking a field as complete --- .../study-download-request.resolver.ts | 9 ++++--- .../study-download-request.service.ts | 25 +++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index fe70ed75..c3bf9a86 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -2,7 +2,7 @@ import { Resolver, Mutation, Args, ResolveField, Parent, ID, Query } from '@nest import { JwtAuthGuard } from '../../jwt/jwt.guard'; import { OrganizationGuard } from '../../organization/organization.guard'; import { UseGuards } from '@nestjs/common'; -import { StudyDownloadRequest } from '../models/study-download-request.model'; +import { StudyDownloadField, StudyDownloadRequest } from '../models/study-download-request.model'; import { StudyDownloadService } from '../services/study-download-request.service'; import { CreateStudyDownloadPipe } from '../pipes/study-download-request-create.pipe'; import { CreateStudyDownloadRequest } from '../dtos/study-download-request-create.dto'; @@ -30,8 +30,11 @@ export class StudyDownloadRequestResolver { } @Mutation(() => Boolean) - async markStudyFieldComplete(@Args('downloadRequest', { type: () => ID }) downloadRequest: StudyDownloadRequest): Promise { - await this.studyDownloadService.markStudyFieldComplete(downloadRequest); + async markStudyFieldComplete( + @Args('downloadRequest', { type: () => ID }) downloadRequest: StudyDownloadRequest, + @Args('studyField', { type: () => StudyDownloadField }) studyField: StudyDownloadField + ): Promise { + await this.studyDownloadService.markStudyFieldComplete(downloadRequest, studyField); return true; } diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 563d7c9a..8a0fe19a 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { StudyDownloadRequest } from '../models/study-download-request.model'; +import { StudyDownloadField, StudyDownloadRequest } from '../models/study-download-request.model'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { CreateStudyDownloadRequest } from '../dtos/study-download-request-create.dto'; @@ -108,8 +108,29 @@ export class StudyDownloadService { return this.downloadRequestModel.findById(id); } - async markStudyFieldComplete(downloadRequest: StudyDownloadRequest): Promise { + /** + * Handles flagging when a field is complete and then updating the status when all fields are complete + */ + async markStudyFieldComplete(downloadRequest: StudyDownloadRequest, studyField: StudyDownloadField): Promise { + // Mark the field as complete + switch(studyField) { + case StudyDownloadField.ENTRY_ZIP: + this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); + break; + case StudyDownloadField.TAGGED_ENTRIES_ZIP: + this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { taggedEntryZipComplete: true }}); + break; + default: + throw new Error(`Unknown field ${studyField}`); + } + const request = (await this.downloadRequestModel.findById(downloadRequest._id))!; + + // Check if all components are complete + if (request.taggedEntryZipComplete && request.entryZipComplete) { + // Mark as complete + this.downloadRequestModel.updateOne({ _id: request._id }, { $set: { status: DownloadStatus.READY }}); + } } /** From 5407aa27ebf48c43ecbd436bbda8035d1b21351a Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 15:18:45 -0400 Subject: [PATCH 4/7] Working marking of fields complete --- .../resolvers/study-download-request.resolver.ts | 3 ++- .../services/study-download-request.service.ts | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index c3bf9a86..956ab655 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -10,6 +10,7 @@ import { OrganizationContext } from '../../organization/organization.context'; import { Organization } from '../../organization/organization.model'; import { StudyPipe } from '../../study/pipes/study.pipe'; import { Study } from '../../study/study.model'; +import { StudyDownloadRequestPipe } from '../pipes/study-download-request.pipe'; @UseGuards(JwtAuthGuard, OrganizationGuard) @Resolver(() => StudyDownloadRequest) @@ -31,7 +32,7 @@ export class StudyDownloadRequestResolver { @Mutation(() => Boolean) async markStudyFieldComplete( - @Args('downloadRequest', { type: () => ID }) downloadRequest: StudyDownloadRequest, + @Args('downloadRequest', { type: () => ID }, StudyDownloadRequestPipe) downloadRequest: StudyDownloadRequest, @Args('studyField', { type: () => StudyDownloadField }) studyField: StudyDownloadField ): Promise { await this.studyDownloadService.markStudyFieldComplete(downloadRequest, studyField); diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 8a0fe19a..44cc4e51 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -115,21 +115,22 @@ export class StudyDownloadService { // Mark the field as complete switch(studyField) { case StudyDownloadField.ENTRY_ZIP: - this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); + console.log('here'); + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); break; case StudyDownloadField.TAGGED_ENTRIES_ZIP: - this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { taggedEntryZipComplete: true }}); + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { taggedEntryZipComplete: true }}); break; default: throw new Error(`Unknown field ${studyField}`); } - const request = (await this.downloadRequestModel.findById(downloadRequest._id))!; + const request = (await this.downloadRequestModel.findOne({ _id: downloadRequest._id }))!; // Check if all components are complete if (request.taggedEntryZipComplete && request.entryZipComplete) { // Mark as complete - this.downloadRequestModel.updateOne({ _id: request._id }, { $set: { status: DownloadStatus.READY }}); + await this.downloadRequestModel.updateOne({ _id: request._id }, { $set: { status: DownloadStatus.READY }}); } } From 2d70e0ffbbccc0f39e565a9a9b803125bafb3f42 Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 15:27:18 -0400 Subject: [PATCH 5/7] Add suepre basic verification step --- .../models/study-download-request.model.ts | 3 +++ .../resolvers/study-download-request.resolver.ts | 10 ++++++++-- .../services/study-download-request.service.ts | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/server/src/download-request/models/study-download-request.model.ts b/packages/server/src/download-request/models/study-download-request.model.ts index b66413d3..8138196b 100644 --- a/packages/server/src/download-request/models/study-download-request.model.ts +++ b/packages/server/src/download-request/models/study-download-request.model.ts @@ -69,6 +69,9 @@ export class StudyDownloadRequest implements DownloadRequest { @Prop({ required: true }) taggedEntryZipComplete: boolean; + + @Prop({ required: true }) + verificationCode: string; } export type StudyDownloadRequestDocument = Document & StudyDownloadRequest; diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index 956ab655..9d56286c 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -1,7 +1,7 @@ import { Resolver, Mutation, Args, ResolveField, Parent, ID, Query } from '@nestjs/graphql'; import { JwtAuthGuard } from '../../jwt/jwt.guard'; import { OrganizationGuard } from '../../organization/organization.guard'; -import { UseGuards } from '@nestjs/common'; +import { UnauthorizedException, UseGuards } from '@nestjs/common'; import { StudyDownloadField, StudyDownloadRequest } from '../models/study-download-request.model'; import { StudyDownloadService } from '../services/study-download-request.service'; import { CreateStudyDownloadPipe } from '../pipes/study-download-request-create.pipe'; @@ -33,8 +33,14 @@ export class StudyDownloadRequestResolver { @Mutation(() => Boolean) async markStudyFieldComplete( @Args('downloadRequest', { type: () => ID }, StudyDownloadRequestPipe) downloadRequest: StudyDownloadRequest, - @Args('studyField', { type: () => StudyDownloadField }) studyField: StudyDownloadField + @Args('studyField', { type: () => StudyDownloadField }) studyField: StudyDownloadField, + @Args('code') verificationCode: string ): Promise { + + if (downloadRequest.verificationCode !== verificationCode) { + throw new UnauthorizedException('Invalid verification code'); + } + await this.studyDownloadService.markStudyFieldComplete(downloadRequest, studyField); return true; } diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 44cc4e51..749c5c41 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -15,6 +15,7 @@ import { VideoFieldService } from '../../tag/services/video-field.service'; import { BucketObjectAction } from 'src/bucket/bucket'; import { Entry } from 'src/entry/models/entry.model'; import { Study } from 'src/study/study.model'; +import { randomUUID } from 'crypto'; @Injectable() export class StudyDownloadService { @@ -41,7 +42,8 @@ export class StudyDownloadService { status: DownloadStatus.IN_PROGRESS, organization: organization._id, entryZipComplete: false, - taggedEntryZipComplete: false + taggedEntryZipComplete: false, + verificationCode: randomUUID() }); const bucketLocation = `${this.downloadService.getPrefix()}/${request._id}`; From a0362fb5d4706d874cdfe625a678b0494951b8dd Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 16:25:07 -0400 Subject: [PATCH 6/7] Working ability to mark download field as complete --- .../download-request.module.ts | 4 +++- .../models/dataset-download-request.model.ts | 17 ++++++++++++- .../pipes/dataset-download-request.pipe.ts | 16 +++++++++++++ .../dataset-download-request.resolver.ts | 20 ++++++++++++++-- .../study-download-request.resolver.ts | 1 - .../dataset-download-request.service.ts | 24 +++++++++++++++++-- 6 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/server/src/download-request/download-request.module.ts b/packages/server/src/download-request/download-request.module.ts index 422863b5..700e4b0e 100644 --- a/packages/server/src/download-request/download-request.module.ts +++ b/packages/server/src/download-request/download-request.module.ts @@ -17,6 +17,7 @@ import { StudyDownloadRequestResolver } from './resolvers/study-download-request import { StudyDownloadService } from './services/study-download-request.service'; import { TagModule } from '../tag/tag.module'; import { StudyDownloadRequestPipe } from './pipes/study-download-request.pipe'; +import { DatasetDownloadRequestPipe } from './pipes/dataset-download-request.pipe'; @Module({ imports: [ @@ -40,7 +41,8 @@ import { StudyDownloadRequestPipe } from './pipes/study-download-request.pipe'; CreateStudyDownloadPipe, StudyDownloadRequestResolver, StudyDownloadService, - StudyDownloadRequestPipe + StudyDownloadRequestPipe, + DatasetDownloadRequestPipe ] }) export class DownloadRequestModule {} diff --git a/packages/server/src/download-request/models/dataset-download-request.model.ts b/packages/server/src/download-request/models/dataset-download-request.model.ts index f3c074de..f9a5963b 100644 --- a/packages/server/src/download-request/models/dataset-download-request.model.ts +++ b/packages/server/src/download-request/models/dataset-download-request.model.ts @@ -1,7 +1,16 @@ import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; import { DownloadStatus, DownloadRequest } from './download-request.model'; -import { ObjectType, Field } from '@nestjs/graphql'; +import { ObjectType, Field, registerEnumType } from '@nestjs/graphql'; + +export enum DatasetDownloadField { + ENTRY_ZIP = 'ENTRY_ZIP' +} + +registerEnumType(DatasetDownloadField, { + name: 'DatasetDownloadField' +}); + @Schema() @ObjectType() @@ -34,6 +43,12 @@ export class DatasetDownloadRequest implements DownloadRequest { @Prop({ required: false }) webhookPayloadLocation?: string; + + @Prop({ required: true }) + entryZipComplete: boolean; + + @Prop({ required: true }) + verificationCode: string; } export type DatasetDownloadRequestDocument = Document & DatasetDownloadRequest; diff --git a/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts b/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts index e69de29b..8c33ee20 100644 --- a/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts +++ b/packages/server/src/download-request/pipes/dataset-download-request.pipe.ts @@ -0,0 +1,16 @@ +import { Injectable, PipeTransform, BadRequestException } from '@nestjs/common'; +import { DatasetDownloadRequest } from '../models/dataset-download-request.model'; +import { DatasetDownloadService } from '../services/dataset-download-request.service'; + +@Injectable() +export class DatasetDownloadRequestPipe implements PipeTransform> { + constructor(private readonly downloadRequestService: DatasetDownloadService) {} + + async transform(value: string): Promise { + const downloadRequest = await this.downloadRequestService.find(value); + if (!downloadRequest) { + throw new BadRequestException(`Dataset download request with id ${value} not found`); + } + return downloadRequest; + } +} diff --git a/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts b/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts index 04ad498b..48866a34 100644 --- a/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts @@ -1,8 +1,8 @@ import { Resolver, Mutation, Args, Query, ID, ResolveField, Parent } from '@nestjs/graphql'; import { CreateDatasetDownloadRequest } from '../dtos/dataset-download-request-create.dto'; -import { DatasetDownloadRequest } from '../models/dataset-download-request.model'; +import { DatasetDownloadRequest, DatasetDownloadField } from '../models/dataset-download-request.model'; import { DatasetDownloadService } from '../services/dataset-download-request.service'; -import { UseGuards } from '@nestjs/common'; +import { UnauthorizedException, UseGuards } from '@nestjs/common'; import { JwtAuthGuard } from '../../jwt/jwt.guard'; import { OrganizationContext } from 'src/organization/organization.context'; import { Organization } from '../../organization/organization.model'; @@ -10,6 +10,7 @@ import { OrganizationGuard } from '../../organization/organization.guard'; import { CreateDatasetDownloadPipe } from '../pipes/dataset-download-request-create.pipe'; import { Dataset } from '../../dataset/dataset.model'; import { DatasetPipe } from '../../dataset/pipes/dataset.pipe'; +import { DatasetDownloadRequestPipe } from '../pipes/dataset-download-request.pipe'; @UseGuards(JwtAuthGuard, OrganizationGuard) @Resolver(() => DatasetDownloadRequest) @@ -34,6 +35,21 @@ export class DatasetDownloadRequestResolver { return this.datasetDownloadService.getDatasetDownloadRequests(dataset); } + @Mutation(() => Boolean) + async markDatasetFieldComplete( + @Args('downloadRequest', { type: () => ID }, DatasetDownloadRequestPipe) downloadRequest: DatasetDownloadRequest, + @Args('datasetField', { type: () => DatasetDownloadField }) datasetField: DatasetDownloadField, + @Args('code') verificationCode: string + ): Promise { + + if (verificationCode !== downloadRequest.verificationCode) { + throw new UnauthorizedException(`Invalid verification code`); + } + + await this.datasetDownloadService.markFieldComplete(downloadRequest, datasetField); + return true; + } + @ResolveField(() => String) async entryZip(@Parent() downloadRequest: DatasetDownloadRequest): Promise { return this.datasetDownloadService.getEntryZipURL(downloadRequest); diff --git a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts index 9d56286c..32a3dc63 100644 --- a/packages/server/src/download-request/resolvers/study-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/study-download-request.resolver.ts @@ -36,7 +36,6 @@ export class StudyDownloadRequestResolver { @Args('studyField', { type: () => StudyDownloadField }) studyField: StudyDownloadField, @Args('code') verificationCode: string ): Promise { - if (downloadRequest.verificationCode !== verificationCode) { throw new UnauthorizedException('Invalid verification code'); } diff --git a/packages/server/src/download-request/services/dataset-download-request.service.ts b/packages/server/src/download-request/services/dataset-download-request.service.ts index 36adc3e0..a86d611f 100644 --- a/packages/server/src/download-request/services/dataset-download-request.service.ts +++ b/packages/server/src/download-request/services/dataset-download-request.service.ts @@ -8,9 +8,10 @@ import { BucketFactory } from '../../bucket/bucket-factory.service'; import { EntryService } from '../../entry/services/entry.service'; import { Organization } from '../../organization/organization.model'; import { CreateDatasetDownloadRequest } from '../dtos/dataset-download-request-create.dto'; -import { DatasetDownloadRequest } from '../models/dataset-download-request.model'; +import { DatasetDownloadField, DatasetDownloadRequest } from '../models/dataset-download-request.model'; import { DownloadRequest, DownloadStatus } from '../models/download-request.model'; import { DownloadRequestService } from './download-request.service'; +import { randomUUID } from 'crypto'; @Injectable() export class DatasetDownloadService { @@ -33,7 +34,9 @@ export class DatasetDownloadService { ...downloadRequest, date: new Date(), status: DownloadStatus.IN_PROGRESS, - organization: organization._id + organization: organization._id, + entryZipComplete: false, + verificationCode: randomUUID() }); const bucketLocation = `${this.downloadService.getPrefix()}/${request._id}`; @@ -88,4 +91,21 @@ export class DatasetDownloadService { new Date(Date.now() + this.expiration) ); } + + async find(id: string): Promise { + return this.downloadRequestModel.findById({ _id: id }); + } + + async markFieldComplete(downloadRequest: DatasetDownloadRequest, field: DatasetDownloadField): Promise { + switch(field) { + case DatasetDownloadField.ENTRY_ZIP: + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); + break; + default: + throw new Error(`Unknown dataset download field ${field}`); + } + + // With only one field supported, can mark the download as complete + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { status: DownloadStatus.READY }}); + } } From dce9fab05fb00b1a43cc33b2ea8135138977df04 Mon Sep 17 00:00:00 2001 From: cbolles Date: Mon, 6 May 2024 16:40:40 -0400 Subject: [PATCH 7/7] Fix formatting --- .../models/dataset-download-request.model.ts | 1 - .../pipes/study-download-request.pipe.ts | 1 - .../resolvers/dataset-download-request.resolver.ts | 1 - .../services/dataset-download-request.service.ts | 6 +++--- .../services/study-download-request.service.ts | 11 +++++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/server/src/download-request/models/dataset-download-request.model.ts b/packages/server/src/download-request/models/dataset-download-request.model.ts index f9a5963b..edef74c2 100644 --- a/packages/server/src/download-request/models/dataset-download-request.model.ts +++ b/packages/server/src/download-request/models/dataset-download-request.model.ts @@ -11,7 +11,6 @@ registerEnumType(DatasetDownloadField, { name: 'DatasetDownloadField' }); - @Schema() @ObjectType() export class DatasetDownloadRequest implements DownloadRequest { diff --git a/packages/server/src/download-request/pipes/study-download-request.pipe.ts b/packages/server/src/download-request/pipes/study-download-request.pipe.ts index b1953d03..0b08dc06 100644 --- a/packages/server/src/download-request/pipes/study-download-request.pipe.ts +++ b/packages/server/src/download-request/pipes/study-download-request.pipe.ts @@ -2,7 +2,6 @@ import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; import { StudyDownloadRequest } from '../models/study-download-request.model'; import { StudyDownloadService } from '../services/study-download-request.service'; - @Injectable() export class StudyDownloadRequestPipe implements PipeTransform> { constructor(private readonly downloadService: StudyDownloadService) {} diff --git a/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts b/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts index 48866a34..354905f8 100644 --- a/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts +++ b/packages/server/src/download-request/resolvers/dataset-download-request.resolver.ts @@ -41,7 +41,6 @@ export class DatasetDownloadRequestResolver { @Args('datasetField', { type: () => DatasetDownloadField }) datasetField: DatasetDownloadField, @Args('code') verificationCode: string ): Promise { - if (verificationCode !== downloadRequest.verificationCode) { throw new UnauthorizedException(`Invalid verification code`); } diff --git a/packages/server/src/download-request/services/dataset-download-request.service.ts b/packages/server/src/download-request/services/dataset-download-request.service.ts index a86d611f..4240b945 100644 --- a/packages/server/src/download-request/services/dataset-download-request.service.ts +++ b/packages/server/src/download-request/services/dataset-download-request.service.ts @@ -97,15 +97,15 @@ export class DatasetDownloadService { } async markFieldComplete(downloadRequest: DatasetDownloadRequest, field: DatasetDownloadField): Promise { - switch(field) { + switch (field) { case DatasetDownloadField.ENTRY_ZIP: - await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true } }); break; default: throw new Error(`Unknown dataset download field ${field}`); } // With only one field supported, can mark the download as complete - await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { status: DownloadStatus.READY }}); + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { status: DownloadStatus.READY } }); } } diff --git a/packages/server/src/download-request/services/study-download-request.service.ts b/packages/server/src/download-request/services/study-download-request.service.ts index 749c5c41..ed86318d 100644 --- a/packages/server/src/download-request/services/study-download-request.service.ts +++ b/packages/server/src/download-request/services/study-download-request.service.ts @@ -115,13 +115,16 @@ export class StudyDownloadService { */ async markStudyFieldComplete(downloadRequest: StudyDownloadRequest, studyField: StudyDownloadField): Promise { // Mark the field as complete - switch(studyField) { + switch (studyField) { case StudyDownloadField.ENTRY_ZIP: console.log('here'); - await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true }}); + await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { entryZipComplete: true } }); break; case StudyDownloadField.TAGGED_ENTRIES_ZIP: - await this.downloadRequestModel.updateOne({ _id: downloadRequest._id }, { $set: { taggedEntryZipComplete: true }}); + await this.downloadRequestModel.updateOne( + { _id: downloadRequest._id }, + { $set: { taggedEntryZipComplete: true } } + ); break; default: throw new Error(`Unknown field ${studyField}`); @@ -132,7 +135,7 @@ export class StudyDownloadService { // Check if all components are complete if (request.taggedEntryZipComplete && request.entryZipComplete) { // Mark as complete - await this.downloadRequestModel.updateOne({ _id: request._id }, { $set: { status: DownloadStatus.READY }}); + await this.downloadRequestModel.updateOne({ _id: request._id }, { $set: { status: DownloadStatus.READY } }); } }