Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 8 additions & 4 deletions src/access/domain/repositories/IAccessRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ import { GuestbookResponseDTO } from '../dtos/GuestbookResponseDTO'
export interface IAccessRepository {
submitGuestbookForDatafileDownload(
fileId: number | string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string>

submitGuestbookForDatafilesDownload(
fileIds: string | Array<number | string>,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string>

submitGuestbookForDatasetDownload(
datasetId: number | string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string>

submitGuestbookForDatasetVersionDownload(
datasetId: number | string,
versionId: string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string>
}
12 changes: 10 additions & 2 deletions src/access/domain/useCases/SubmitGuestbookForDatafileDownload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ export class SubmitGuestbookForDatafileDownload implements UseCase<string> {
* @param {GuestbookResponseDTO} guestbookResponse - Guestbook response payload.
* @returns {Promise<string>} - Signed URL for the download.
*/
async execute(fileId: number | string, guestbookResponse: GuestbookResponseDTO): Promise<string> {
return await this.accessRepository.submitGuestbookForDatafileDownload(fileId, guestbookResponse)
async execute(
fileId: number | string,
Comment thread
ekraffmiller marked this conversation as resolved.
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
return await this.accessRepository.submitGuestbookForDatafileDownload(
fileId,
guestbookResponse,
format
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ export class SubmitGuestbookForDatafilesDownload implements UseCase<string> {
*/
async execute(
fileIds: string | Array<number | string>,
Comment thread
ekraffmiller marked this conversation as resolved.
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
return await this.accessRepository.submitGuestbookForDatafilesDownload(
fileIds,
guestbookResponse
guestbookResponse,
format
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ export class SubmitGuestbookForDatasetDownload implements UseCase<string> {
*/
async execute(
datasetId: number | string,
Comment thread
ekraffmiller marked this conversation as resolved.
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
return await this.accessRepository.submitGuestbookForDatasetDownload(
datasetId,
guestbookResponse
guestbookResponse,
format
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export class SubmitGuestbookForDatasetVersionDownload implements UseCase<string>
async execute(
datasetId: number | string,
versionId: string,
Comment thread
ekraffmiller marked this conversation as resolved.
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
return await this.accessRepository.submitGuestbookForDatasetVersionDownload(
datasetId,
versionId,
guestbookResponse
guestbookResponse,
format
)
}
}
28 changes: 20 additions & 8 deletions src/access/infra/repositories/AccessRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ export class AccessRepository extends ApiRepository implements IAccessRepository

public async submitGuestbookForDatafileDownload(
fileId: number | string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
const endpoint = this.buildApiEndpoint(`${this.accessResourceName}/datafile`, undefined, fileId)
return this.doPost(endpoint, guestbookResponse, { signed: true })
const queryParams = format ? { signed: true, format } : { signed: true }

return this.doPost(endpoint, guestbookResponse, queryParams)
.then((response) => {
const signedUrl = response.data.data.signedUrl
return signedUrl
Expand All @@ -22,15 +25,18 @@ export class AccessRepository extends ApiRepository implements IAccessRepository

public async submitGuestbookForDatafilesDownload(
fileIds: Array<number>,
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fileIds parameter type here is narrower than the interface (IAccessRepository allows string | Array<number | string>). This makes Array.isArray(fileIds) effectively dead-code from a typing perspective and prevents callers using AccessRepository directly from passing a comma-separated string. Update this method signature (and any related typing) to match IAccessRepository and keep the Array.isArray branch meaningful.

Suggested change
fileIds: Array<number>,
fileIds: string | Array<number | string>,

Copilot uses AI. Check for mistakes.
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
const queryParams = format ? { signed: true, format } : { signed: true }

return this.doPost(
this.buildApiEndpoint(
this.accessResourceName,
`datafiles/${Array.isArray(fileIds) ? fileIds.join(',') : fileIds}`
),
guestbookResponse,
{ signed: true }
queryParams
)
.then((response) => {
const signedUrl = response.data.data.signedUrl
Expand All @@ -43,14 +49,17 @@ export class AccessRepository extends ApiRepository implements IAccessRepository

public async submitGuestbookForDatasetDownload(
datasetId: number | string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
const endpoint = this.buildApiEndpoint(
`${this.accessResourceName}/dataset`,
undefined,
datasetId
)
return this.doPost(endpoint, guestbookResponse, { signed: true })
const queryParams = format ? { signed: true, format } : { signed: true }

return this.doPost(endpoint, guestbookResponse, queryParams)
.then((response) => {
const signedUrl = response.data.data.signedUrl
return signedUrl
Expand All @@ -63,14 +72,17 @@ export class AccessRepository extends ApiRepository implements IAccessRepository
public async submitGuestbookForDatasetVersionDownload(
datasetId: number | string,
versionId: string,
guestbookResponse: GuestbookResponseDTO
guestbookResponse: GuestbookResponseDTO,
format?: string
): Promise<string> {
const endpoint = this.buildApiEndpoint(
`${this.accessResourceName}/dataset`,
`versions/${versionId}`,
datasetId
)
return this.doPost(endpoint, guestbookResponse, { signed: true })
const queryParams = format ? { signed: true, format } : { signed: true }

return this.doPost(endpoint, guestbookResponse, queryParams)
.then((response) => {
const signedUrl = response.data.data.signedUrl
return signedUrl
Expand Down
11 changes: 11 additions & 0 deletions test/integration/access/AccessRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ describe('AccessRepository', () => {
expect(() => new URL(actual)).not.toThrow()
})

test('should preserve format=tab in signed url for dataset download', async () => {
const actual = await sut.submitGuestbookForDatasetDownload(
testDatasetIds.numericId,
guestbookResponse,
'original'
)
Comment thread
ekraffmiller marked this conversation as resolved.
Outdated
const signedUrl = new URL(actual)

expect(signedUrl.searchParams.get('format')).toEqual('original')
})

test('should return error when dataset does not exist', async () => {
const nonExistentId = 999999999
await expect(
Expand Down
87 changes: 84 additions & 3 deletions test/unit/access/SubmitGuestbookDownloads.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,31 @@ describe('access download use cases', () => {

const actual = await sut.execute(1, guestbookResponse)

expect(repository.submitGuestbookForDatafileDownload).toHaveBeenCalledWith(1, guestbookResponse)
expect(repository.submitGuestbookForDatafileDownload).toHaveBeenCalledWith(
1,
guestbookResponse,
undefined
)
expect(actual).toEqual('https://signed.datafile')
})

test('should submit datafile download with format and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatafileDownload = jest
.fn()
.mockResolvedValue('https://signed.datafile?format=original')
const sut = new SubmitGuestbookForDatafileDownload(repository)

const actual = await sut.execute(1, guestbookResponse, 'original')

expect(repository.submitGuestbookForDatafileDownload).toHaveBeenCalledWith(
1,
guestbookResponse,
'original'
)
expect(actual).toEqual('https://signed.datafile?format=original')
})

test('should submit datafiles download and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatafilesDownload = jest
Expand All @@ -37,11 +58,29 @@ describe('access download use cases', () => {

expect(repository.submitGuestbookForDatafilesDownload).toHaveBeenCalledWith(
[1, 2],
guestbookResponse
guestbookResponse,
undefined
)
expect(actual).toEqual('https://signed.datafiles')
})

test('should submit datafiles download with format and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatafilesDownload = jest
.fn()
.mockResolvedValue('https://signed.datafiles?format=original')
const sut = new SubmitGuestbookForDatafilesDownload(repository)

const actual = await sut.execute([1, 2], guestbookResponse, 'original')

expect(repository.submitGuestbookForDatafilesDownload).toHaveBeenCalledWith(
[1, 2],
guestbookResponse,
'original'
)
expect(actual).toEqual('https://signed.datafiles?format=original')
})

test('should submit dataset download and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatasetDownload = jest
Expand All @@ -53,11 +92,29 @@ describe('access download use cases', () => {

expect(repository.submitGuestbookForDatasetDownload).toHaveBeenCalledWith(
'doi:10.5072/FK2/TEST',
guestbookResponse
guestbookResponse,
undefined
)
expect(actual).toEqual('https://signed.dataset')
})

test('should submit dataset download with format and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatasetDownload = jest
.fn()
.mockResolvedValue('https://signed.dataset?format=original')
const sut = new SubmitGuestbookForDatasetDownload(repository)

const actual = await sut.execute('doi:10.5072/FK2/TEST', guestbookResponse, 'original')

expect(repository.submitGuestbookForDatasetDownload).toHaveBeenCalledWith(
'doi:10.5072/FK2/TEST',
guestbookResponse,
'original'
)
expect(actual).toEqual('https://signed.dataset?format=original')
})

test('should throw WriteError when dataset version download fails', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatasetVersionDownload = jest
Expand All @@ -66,5 +123,29 @@ describe('access download use cases', () => {
const sut = new SubmitGuestbookForDatasetVersionDownload(repository)

await expect(sut.execute(10, '2.0', guestbookResponse)).rejects.toThrow(WriteError)
expect(repository.submitGuestbookForDatasetVersionDownload).toHaveBeenCalledWith(
10,
'2.0',
guestbookResponse,
undefined
)
})

test('should submit dataset version download with format and return signed url', async () => {
const repository: IAccessRepository = {} as IAccessRepository
repository.submitGuestbookForDatasetVersionDownload = jest
.fn()
.mockResolvedValue('https://signed.dataset.version?format=original')
const sut = new SubmitGuestbookForDatasetVersionDownload(repository)

const actual = await sut.execute(10, '2.0', guestbookResponse, 'original')

expect(repository.submitGuestbookForDatasetVersionDownload).toHaveBeenCalledWith(
10,
'2.0',
guestbookResponse,
'original'
)
expect(actual).toEqual('https://signed.dataset.version?format=original')
})
})
Loading