-
Notifications
You must be signed in to change notification settings - Fork 5
KMS-598: Support Older Versions of the GCMD Keywords in KMS #131
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
Open
htranho
wants to merge
7
commits into
main
Choose a base branch
from
KMS-598
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2af6cb8
KMS-598: Support Older Versions of the GCMD Keywords in KMS
htranho e92e380
KMS-598: Add input validation
htranho ee63e36
KMS-598: Update error messages
htranho cb9a6aa
KMS-598: Updated error messages
htranho abb9e04
KMS-598: Update S3 bucket name, getCapabilities and condition for lis…
htranho 0ab1dbe
KMS-598: Use pagination
htranho 25348dc
KMS-598: Add test
htranho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
serverless/src/getHistoricalConceptVersions/__tests__/handler.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { | ||
| beforeEach, | ||
| describe, | ||
| expect, | ||
| vi | ||
| } from 'vitest' | ||
|
|
||
| import { getApplicationConfig } from '@/shared/getConfig' | ||
| import { logAnalyticsData } from '@/shared/logAnalyticsData' | ||
|
|
||
| import { getHistoricalConceptVersions } from '../handler' | ||
|
|
||
| // `getS3Client()` is called once at module-load time in handler.js, so the | ||
| // mocked client needs to exist before the handler module is imported. Using | ||
| // vi.hoisted + a mock factory ensures `mockSend` is available when the | ||
| // `@/shared/awsClients` mock is set up, and lets us control its behavior | ||
| // per test via mockSend.mockResolvedValueOnce(...). | ||
| const { mockSend } = vi.hoisted(() => ({ | ||
| mockSend: vi.fn() | ||
| })) | ||
|
|
||
| vi.mock('@/shared/awsClients', () => ({ | ||
| getS3Client: () => ({ send: mockSend }) | ||
| })) | ||
|
|
||
| vi.mock('@/shared/getConfig') | ||
| vi.mock('@/shared/logAnalyticsData') | ||
|
|
||
| describe('getHistoricalConceptVersions', () => { | ||
| beforeEach(() => { | ||
| vi.resetAllMocks() | ||
| vi.spyOn(console, 'error').mockImplementation(() => {}) | ||
|
|
||
| // Mock getApplicationConfig | ||
| getApplicationConfig.mockReturnValue({ | ||
| defaultResponseHeaders: { 'X-Test': 'test-header' } | ||
| }) | ||
| }) | ||
|
|
||
| describe('when successful', () => { | ||
| test('should return 200 with the list of version directories', async () => { | ||
| mockSend.mockResolvedValueOnce({ | ||
| CommonPrefixes: [ | ||
| { Prefix: 'A/' }, | ||
| { Prefix: 'B/' } | ||
| ] | ||
| }) | ||
|
|
||
| const event = {} | ||
| const context = {} | ||
| const response = await getHistoricalConceptVersions(event, context) | ||
|
|
||
| expect(response.statusCode).toBe(200) | ||
| expect(response.headers['X-Test']).toBe('test-header') | ||
| expect(JSON.parse(response.body)).toEqual({ historicalVersions: ['A', 'B'] }) | ||
| }) | ||
|
|
||
| test('should exclude the draft directory', async () => { | ||
| mockSend.mockResolvedValueOnce({ | ||
| CommonPrefixes: [ | ||
| { Prefix: 'draft/' }, | ||
| { Prefix: 'A/' } | ||
| ] | ||
| }) | ||
|
|
||
| const response = await getHistoricalConceptVersions({}, {}) | ||
|
|
||
| expect(JSON.parse(response.body)).toEqual({ historicalVersions: ['A'] }) | ||
| }) | ||
|
|
||
| test('should return an empty array when there are no common prefixes', async () => { | ||
| mockSend.mockResolvedValueOnce({}) | ||
|
|
||
| const response = await getHistoricalConceptVersions({}, {}) | ||
|
|
||
| expect(response.statusCode).toBe(200) | ||
| expect(JSON.parse(response.body)).toEqual({ historicalVersions: [] }) | ||
| }) | ||
|
|
||
| test('should paginate through multiple pages of results', async () => { | ||
| mockSend | ||
| .mockResolvedValueOnce({ | ||
| CommonPrefixes: [{ Prefix: 'A/' }], | ||
| NextContinuationToken: 'page-2-token' | ||
| }) | ||
| .mockResolvedValueOnce({ | ||
| CommonPrefixes: [{ Prefix: 'B/' }] | ||
| }) | ||
|
|
||
| const response = await getHistoricalConceptVersions({}, {}) | ||
|
|
||
| expect(mockSend).toHaveBeenCalledTimes(2) | ||
| expect(JSON.parse(response.body)).toEqual({ historicalVersions: ['A', 'B'] }) | ||
| }) | ||
|
|
||
| test('should call logAnalyticsData with the event and context', async () => { | ||
| mockSend.mockResolvedValueOnce({ CommonPrefixes: [] }) | ||
|
|
||
| const event = { some: 'event' } | ||
| const context = { some: 'context' } | ||
| await getHistoricalConceptVersions(event, context) | ||
|
|
||
| expect(logAnalyticsData).toHaveBeenCalledWith({ | ||
| event, | ||
| context | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| describe('when unsuccessful', () => { | ||
| test('should return 500 when the S3 request fails', async () => { | ||
| mockSend.mockRejectedValueOnce(new Error('The specified bucket does not exist')) | ||
|
|
||
| const response = await getHistoricalConceptVersions({}, {}) | ||
|
|
||
| expect(response.statusCode).toBe(500) | ||
| expect(response.headers['X-Test']).toBe('test-header') | ||
| expect(JSON.parse(response.body)).toEqual({ | ||
| message: 'Failed to fetch version directories' | ||
| }) | ||
| }) | ||
|
|
||
| test('should log the error to the console', async () => { | ||
| const error = new Error('The specified bucket does not exist') | ||
| mockSend.mockRejectedValueOnce(error) | ||
|
|
||
| await getHistoricalConceptVersions({}, {}) | ||
|
|
||
| // eslint-disable-next-line no-console | ||
| expect(console.error).toHaveBeenCalledWith( | ||
| 'Failed to list S3 version directories:', | ||
| error.message | ||
| ) | ||
| }) | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import { ListObjectsV2Command } from '@aws-sdk/client-s3' | ||
|
|
||
| import { getS3Client } from '@/shared/awsClients' | ||
| import { getApplicationConfig } from '@/shared/getConfig' | ||
| import { logAnalyticsData } from '@/shared/logAnalyticsData' | ||
|
|
||
| /** | ||
| * S3 bucket name to list version directories from | ||
| * @type {string} | ||
| */ | ||
| const bucketName = process.env.S3_BUCKET_NAME || 'kms-rdf-backup-sit' | ||
|
|
||
| /** | ||
| * S3 Client from shared configuration | ||
| * @type {S3Client} | ||
| */ | ||
| const s3Client = getS3Client() | ||
|
|
||
| /** | ||
| * Lists top-level "directory" names in the S3 bucket by using a delimiter | ||
| * so S3 groups keys into CommonPrefixes (its equivalent of folders), | ||
| * excluding the "draft/" prefix. | ||
| * | ||
| * @returns {Promise<Array<string>>} Array of version/directory names | ||
| */ | ||
| const listVersionDirectories = async () => { | ||
|
htranho marked this conversation as resolved.
|
||
| const historicalVersions = [] | ||
| let continuationToken | ||
|
|
||
| /* eslint-disable no-await-in-loop */ | ||
| do { | ||
| const command = new ListObjectsV2Command({ | ||
| Bucket: bucketName, | ||
| Delimiter: '/', | ||
| ContinuationToken: continuationToken | ||
| }) | ||
|
|
||
| const response = await s3Client.send(command) | ||
|
|
||
| if (response.CommonPrefixes) { | ||
|
htranho marked this conversation as resolved.
Outdated
|
||
| const prefixes = response.CommonPrefixes | ||
| .map((p) => p.Prefix) | ||
| .filter(Boolean) | ||
| .map((prefix) => prefix.replace(/\/$/, '')) // Strip trailing slash | ||
| .filter((name) => name !== 'draft') // Exclude the draft "directory" | ||
|
|
||
| historicalVersions.push(...prefixes) | ||
| } | ||
|
|
||
| continuationToken = response.NextContinuationToken | ||
| } while (continuationToken) | ||
| /* eslint-enable no-await-in-loop */ | ||
|
|
||
| return historicalVersions | ||
| } | ||
|
|
||
| export const getHistoricalConceptVersions = async (event, context) => { | ||
| const { defaultResponseHeaders } = getApplicationConfig() | ||
|
|
||
| logAnalyticsData({ | ||
| event, | ||
| context | ||
| }) | ||
|
|
||
| try { | ||
| const historicalVersions = await listVersionDirectories() | ||
|
|
||
| return { | ||
| statusCode: 200, | ||
| headers: defaultResponseHeaders, | ||
| body: JSON.stringify({ historicalVersions }) | ||
| } | ||
| } catch (error) { | ||
| console.error('Failed to list S3 version directories:', error.message) | ||
|
|
||
| return { | ||
| statusCode: 500, | ||
| headers: defaultResponseHeaders, | ||
| body: JSON.stringify({ message: 'Failed to fetch version directories' }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default getHistoricalConceptVersions | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.