diff --git a/serverless-configs/aws-functions.yml b/serverless-configs/aws-functions.yml
index 458fafe..a1748be 100644
--- a/serverless-configs/aws-functions.yml
+++ b/serverless-configs/aws-functions.yml
@@ -37,7 +37,7 @@ createConcept:
- http:
method: post
cors: ${file(./serverless-configs/${self:provider.name}-cors-configuration.yml)}
- path: concept/{conceptId}
+ path: concept
createConcepts:
handler: serverless/src/createConcepts/handler.default
timeout: ${env:LAMBDA_TIMEOUT, '30'}
@@ -53,7 +53,7 @@ updateConcept:
- http:
method: put
cors: ${file(./serverless-configs/${self:provider.name}-cors-configuration.yml)}
- path: concept/{conceptId}
+ path: concept
deleteConcept:
handler: serverless/src/deleteConcept/handler.default
diff --git a/serverless/src/createConcept/__tests__/handler.test.js b/serverless/src/createConcept/__tests__/handler.test.js
index 6b43680..bf26d31 100644
--- a/serverless/src/createConcept/__tests__/handler.test.js
+++ b/serverless/src/createConcept/__tests__/handler.test.js
@@ -6,90 +6,165 @@ import {
} from 'vitest'
import createConcept from '../handler'
import conceptIdExists from '../../utils/conceptIdExists'
+import getConceptId from '../../utils/getConceptId'
import { getApplicationConfig } from '../../utils/getConfig'
import { sparqlRequest } from '../../utils/sparqlRequest'
// Mock the dependencies
vi.mock('../../utils/conceptIdExists')
+vi.mock('../../utils/getConceptId')
vi.mock('../../utils/getConfig')
vi.mock('../../utils/sparqlRequest')
describe('createConcept', () => {
- const mockEvent = {
- body: '...',
- pathParameters: { conceptId: '123' }
- }
-
+ const mockRdfXml = '...'
+ const mockEvent = { body: mockRdfXml }
const mockDefaultHeaders = { 'Content-Type': 'application/json' }
- beforeAll(() => {
- vi.spyOn(console, 'error').mockImplementation(() => {})
- vi.spyOn(console, 'log').mockImplementation(() => {})
- })
+ const mockConceptId = '123'
+ const mockConceptIRI = `https://gcmd.earthdata.nasa.gov/kms/concept/${mockConceptId}`
beforeEach(() => {
vi.resetAllMocks()
getApplicationConfig.mockReturnValue({ defaultResponseHeaders: mockDefaultHeaders })
+ getConceptId.mockReturnValue(mockConceptId)
+ vi.spyOn(console, 'log').mockImplementation(() => {})
+ vi.spyOn(console, 'error').mockImplementation(() => {})
})
- test('should return 404 if concept already exists', async () => {
- conceptIdExists.mockResolvedValue(true)
+ test('should handle missing body in event', async () => {
+ const eventWithoutBody = {}
- const result = await createConcept(mockEvent)
+ const result = await createConcept(eventWithoutBody)
expect(result).toEqual({
- statusCode: 404,
- body: JSON.stringify({ message: 'Concept https://gcmd.earthdata.nasa.gov/kms/concept/123 already exists.' }),
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'Missing RDF/XML data in request body'
+ }),
headers: mockDefaultHeaders
})
})
- test('should create concept and return 200 if concept does not exist', async () => {
+ test('should successfully create a concept', async () => {
conceptIdExists.mockResolvedValue(false)
sparqlRequest.mockResolvedValue({ ok: true })
const result = await createConcept(mockEvent)
+ expect(getConceptId).toHaveBeenCalledWith(mockRdfXml)
+ expect(conceptIdExists).toHaveBeenCalledWith(mockConceptIRI)
expect(sparqlRequest).toHaveBeenCalledWith({
contentType: 'application/rdf+xml',
accept: 'application/rdf+xml',
path: '/statements',
method: 'POST',
- body: mockEvent.body
+ body: mockRdfXml
+ })
+
+ expect(result).toEqual({
+ statusCode: 201,
+ body: JSON.stringify({
+ message: 'Successfully created concept',
+ conceptId: mockConceptId
+ }),
+ headers: mockDefaultHeaders
})
+ })
+
+ test('should return 409 if concept already exists', async () => {
+ conceptIdExists.mockResolvedValue(true)
+
+ const result = await createConcept(mockEvent)
expect(result).toEqual({
- statusCode: 200,
- body: 'Successfully loaded RDF XML into RDF4J',
+ statusCode: 409,
+ body: JSON.stringify({ message: `Concept ${mockConceptIRI} already exists.` }),
headers: mockDefaultHeaders
})
})
- test('should return 500 if sparqlRequest fails', async () => {
+ test('should handle getConceptId throwing an error', async () => {
+ getConceptId.mockImplementation(() => {
+ throw new Error('Invalid XML')
+ })
+
+ const result = await createConcept(mockEvent)
+
+ expect(result).toEqual({
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'Invalid XML'
+ }),
+ headers: mockDefaultHeaders
+ })
+ })
+
+ test('should handle missing concept ID', async () => {
+ getConceptId.mockReturnValue(null)
+
+ const result = await createConcept(mockEvent)
+
+ expect(result).toEqual({
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'Invalid or missing concept ID'
+ }),
+ headers: mockDefaultHeaders
+ })
+ })
+
+ test('should handle sparqlRequest failure', async () => {
conceptIdExists.mockResolvedValue(false)
sparqlRequest.mockResolvedValue({
ok: false,
status: 500,
- text: () => Promise.resolve('Server error')
+ text: async () => 'Internal Server Error'
+ })
+
+ const result = await createConcept(mockEvent)
+
+ expect(result).toEqual({
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'HTTP error! status: 500'
+ }),
+ headers: mockDefaultHeaders
})
+ expect(console.log).toHaveBeenCalledWith('Response text:', 'Internal Server Error')
+ })
+
+ test('should handle conceptIdExists throwing an error', async () => {
+ conceptIdExists.mockRejectedValue(new Error('Database error'))
+
const result = await createConcept(mockEvent)
expect(result).toEqual({
- statusCode: 500,
- body: 'Error loading RDF XML into RDF4J',
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'Database error'
+ }),
headers: mockDefaultHeaders
})
})
- test('should return 500 if an error is thrown', async () => {
+ test('should handle sparqlRequest throwing an error', async () => {
conceptIdExists.mockResolvedValue(false)
sparqlRequest.mockRejectedValue(new Error('Network error'))
const result = await createConcept(mockEvent)
expect(result).toEqual({
- statusCode: 500,
- body: 'Error loading RDF XML into RDF4J',
+ statusCode: 400,
+ body: JSON.stringify({
+ message: 'Error creating concept',
+ error: 'Network error'
+ }),
headers: mockDefaultHeaders
})
})
diff --git a/serverless/src/createConcept/handler.js b/serverless/src/createConcept/handler.js
index a17e198..fadded5 100644
--- a/serverless/src/createConcept/handler.js
+++ b/serverless/src/createConcept/handler.js
@@ -1,4 +1,5 @@
import conceptIdExists from '../utils/conceptIdExists'
+import getConceptId from '../utils/getConceptId'
import { getApplicationConfig } from '../utils/getConfig'
import { sparqlRequest } from '../utils/sparqlRequest'
@@ -12,8 +13,6 @@ import { sparqlRequest } from '../utils/sparqlRequest'
* @function createConcept
* @param {Object} event - The Lambda event object.
* @param {string} event.body - The RDF/XML representation of the concept to be created.
- * @param {Object} event.pathParameters - The path parameters from the API Gateway event.
- * @param {string} event.pathParameters.conceptId - The ID of the concept to be created.
* @returns {Promise