|
|
@@ -7,7 +7,7 @@ import {IncomingMessage, ServerResponse} from 'http'; |
|
|
|
export const handleHasMethodAndUrl: Middleware = ({}) => (req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (!req.method) { |
|
|
|
res.writeHead(constants.HTTP_STATUS_METHOD_NOT_ALLOWED, { |
|
|
|
'Allow': 'HEAD,GET,POST,PUT,PATCH,DELETE' // TODO check with resources on allowed methods |
|
|
|
'Allow': 'HEAD, GET, POST, PUT, PATCH, DELETE' |
|
|
|
}); |
|
|
|
res.end(); |
|
|
|
return { |
|
|
@@ -34,21 +34,7 @@ export const handleGetRoot: Middleware = ({ |
|
|
|
responseBodySerializerPair, |
|
|
|
serverParams, |
|
|
|
responseMediaType, |
|
|
|
method, |
|
|
|
url, |
|
|
|
}) => (_req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (method !== 'GET') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
if (url !== '/') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const singleResDatum = { |
|
|
|
name: appParams.name |
|
|
|
}; |
|
|
@@ -76,13 +62,6 @@ export const handleGetCollection: Middleware = ({ |
|
|
|
responseBodySerializerPair, |
|
|
|
responseMediaType, |
|
|
|
}) => async (req: IncomingMessage, res: ServerResponse) => { |
|
|
|
const method = getMethod(req); |
|
|
|
if (method !== 'GET') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const baseUrl = serverParams.baseUrl ?? ''; |
|
|
|
const { url } = getUrl(req, baseUrl); |
|
|
|
|
|
|
@@ -102,6 +81,7 @@ export const handleGetCollection: Middleware = ({ |
|
|
|
|
|
|
|
try { |
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
// TODO querying mechanism |
|
|
|
const resData = await theResource.dataSource.getMultiple(); // TODO paginated responses per resource |
|
|
|
const theFormatted = responseBodySerializerPair.serialize(resData); |
|
|
|
|
|
|
@@ -179,36 +159,15 @@ export const handleGetItem: Middleware = ({ |
|
|
|
|
|
|
|
|
|
|
|
export const handleDeleteItem: Middleware = ({ |
|
|
|
appState, |
|
|
|
method, |
|
|
|
url, |
|
|
|
resource, |
|
|
|
resourceId, |
|
|
|
}) => async (_req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (method !== 'DELETE') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const [, mainResourceRouteName, mainResourceId = ''] = url.split('/'); |
|
|
|
if (mainResourceId === '') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const theResource = Array.from(appState.resources).find((r) => r.routeName === mainResourceRouteName); |
|
|
|
if (typeof theResource === 'undefined') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
const response = await theResource.dataSource.delete(mainResourceId); |
|
|
|
if (typeof response !== 'undefined' && !response && theResource.throws404OnDeletingNotFound) { |
|
|
|
await resource.dataSource.initialize(); |
|
|
|
const response = await resource.dataSource.delete(resourceId); |
|
|
|
if (typeof response !== 'undefined' && !response && resource.throws404OnDeletingNotFound) { |
|
|
|
res.statusCode = constants.HTTP_STATUS_NOT_FOUND; |
|
|
|
res.statusMessage = `${theResource.itemName} Not Found`; |
|
|
|
res.statusMessage = `${resource.itemName} Not Found`; |
|
|
|
} else { |
|
|
|
res.statusCode = constants.HTTP_STATUS_NO_CONTENT; |
|
|
|
} |
|
|
@@ -230,31 +189,11 @@ export const handleDeleteItem: Middleware = ({ |
|
|
|
|
|
|
|
export const handlePatchItem: Middleware = ({ |
|
|
|
appState, |
|
|
|
method, |
|
|
|
url, |
|
|
|
responseBodySerializerPair, |
|
|
|
responseMediaType, |
|
|
|
resource, |
|
|
|
resourceId, |
|
|
|
}) => async (req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (method !== 'PATCH') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const [, mainResourceRouteName, mainResourceId = ''] = url.split('/'); |
|
|
|
if (mainResourceId === '') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const theResource = Array.from(appState.resources).find((r) => r.routeName === mainResourceRouteName); |
|
|
|
if (typeof theResource === 'undefined') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const { deserializerPair: requestBodyDeserializerPair, encodingPair: requestBodyEncodingPair } = getDeserializerObjects(appState, req); |
|
|
|
if (typeof requestBodyDeserializerPair === 'undefined' || typeof requestBodyEncodingPair === 'undefined') { |
|
|
|
res.statusCode = constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE; |
|
|
@@ -264,11 +203,11 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
const existing = await theResource.dataSource.getSingle(mainResourceId); |
|
|
|
await resource.dataSource.initialize(); |
|
|
|
const existing = await resource.dataSource.getSingle(resourceId); |
|
|
|
if (!existing) { |
|
|
|
res.statusCode = constants.HTTP_STATUS_NOT_FOUND; |
|
|
|
res.statusMessage = `${theResource.itemName} Not Found`; |
|
|
|
res.statusMessage = `${resource.itemName} Not Found`; |
|
|
|
res.end(); |
|
|
|
return { |
|
|
|
handled: true |
|
|
@@ -277,7 +216,7 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
|
|
|
|
let bodyDeserialized: unknown; |
|
|
|
try { |
|
|
|
const schema = theResource.schema.type === 'object' ? theResource.schema as v.ObjectSchema<any> : theResource.schema |
|
|
|
const schema = resource.schema.type === 'object' ? resource.schema as v.ObjectSchema<any> : resource.schema |
|
|
|
bodyDeserialized = await getBody( |
|
|
|
req, |
|
|
|
requestBodyDeserializerPair, |
|
|
@@ -293,7 +232,7 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
} catch (errRaw) { |
|
|
|
const err = errRaw as v.ValiError; |
|
|
|
res.statusCode = constants.HTTP_STATUS_BAD_REQUEST; |
|
|
|
res.statusMessage = `Invalid ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Invalid ${resource.itemName}`; |
|
|
|
|
|
|
|
if (Array.isArray(err.issues)) { |
|
|
|
// TODO better error reporting, localizable messages |
|
|
@@ -313,8 +252,8 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
|
|
|
|
try { |
|
|
|
const params = bodyDeserialized as Record<string, unknown>; |
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
const newObject = await theResource.dataSource.patch(mainResourceId, params); |
|
|
|
await resource.dataSource.initialize(); |
|
|
|
const newObject = await resource.dataSource.patch(resourceId, params); |
|
|
|
const theFormatted = responseBodySerializerPair.serialize(newObject); |
|
|
|
res.writeHead(constants.HTTP_STATUS_OK, { |
|
|
|
'Content-Type': responseMediaType, |
|
|
@@ -322,7 +261,7 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
res.end(theFormatted); |
|
|
|
} catch { |
|
|
|
res.statusCode = constants.HTTP_STATUS_INTERNAL_SERVER_ERROR; |
|
|
|
res.statusMessage = `Could Not Return ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Could Not Return ${resource.itemName}`; |
|
|
|
res.end(); |
|
|
|
} |
|
|
|
return { |
|
|
@@ -333,31 +272,10 @@ export const handlePatchItem: Middleware = ({ |
|
|
|
export const handleCreateItem: Middleware = ({ |
|
|
|
appState, |
|
|
|
serverParams, |
|
|
|
method, |
|
|
|
url, |
|
|
|
responseMediaType, |
|
|
|
responseBodySerializerPair, |
|
|
|
resource, |
|
|
|
}) => async (req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (method !== 'POST') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const [, mainResourceRouteName, mainResourceId = ''] = url.split('/'); |
|
|
|
if (mainResourceId !== '') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const theResource = Array.from(appState.resources).find((r) => r.routeName === mainResourceRouteName); |
|
|
|
if (typeof theResource === 'undefined') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const { deserializerPair: requestBodyDeserializerPair, encodingPair: requestBodyEncodingPair } = getDeserializerObjects(appState, req); |
|
|
|
if (typeof requestBodyDeserializerPair === 'undefined' || typeof requestBodyEncodingPair === 'undefined') { |
|
|
|
res.statusCode = constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE; |
|
|
@@ -369,11 +287,11 @@ export const handleCreateItem: Middleware = ({ |
|
|
|
|
|
|
|
let bodyDeserialized: unknown; |
|
|
|
try { |
|
|
|
bodyDeserialized = await getBody(req, requestBodyDeserializerPair, requestBodyEncodingPair, theResource.schema); |
|
|
|
bodyDeserialized = await getBody(req, requestBodyDeserializerPair, requestBodyEncodingPair, resource.schema); |
|
|
|
} catch (errRaw) { |
|
|
|
const err = errRaw as v.ValiError; |
|
|
|
res.statusCode = constants.HTTP_STATUS_BAD_REQUEST; |
|
|
|
res.statusMessage = `Invalid ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Invalid ${resource.itemName}`; |
|
|
|
|
|
|
|
if (Array.isArray(err.issues)) { |
|
|
|
// TODO better error reporting, localizable messages |
|
|
@@ -392,20 +310,20 @@ export const handleCreateItem: Middleware = ({ |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
const newId = await theResource.newId(theResource.dataSource); |
|
|
|
await resource.dataSource.initialize(); |
|
|
|
const newId = await resource.newId(resource.dataSource); |
|
|
|
const params = bodyDeserialized as Record<string, unknown>; |
|
|
|
params[theResource.idAttr] = newId; |
|
|
|
const newObject = await theResource.dataSource.create(params); |
|
|
|
params[resource.idAttr] = newId; |
|
|
|
const newObject = await resource.dataSource.create(params); |
|
|
|
const theFormatted = responseBodySerializerPair.serialize(newObject); |
|
|
|
res.writeHead(constants.HTTP_STATUS_OK, { |
|
|
|
'Content-Type': responseMediaType, |
|
|
|
'Location': `${serverParams.baseUrl}/${theResource.routeName}/${newId}` |
|
|
|
'Location': `${serverParams.baseUrl}/${resource.routeName}/${newId}` |
|
|
|
}); |
|
|
|
res.end(theFormatted); |
|
|
|
} catch { |
|
|
|
res.statusCode = constants.HTTP_STATUS_INTERNAL_SERVER_ERROR; |
|
|
|
res.statusMessage = `Could Not Return ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Could Not Return ${resource.itemName}`; |
|
|
|
res.end(); |
|
|
|
} |
|
|
|
return { |
|
|
@@ -416,31 +334,11 @@ export const handleCreateItem: Middleware = ({ |
|
|
|
export const handleEmplaceItem: Middleware = ({ |
|
|
|
appState, |
|
|
|
serverParams, |
|
|
|
method, |
|
|
|
url, |
|
|
|
responseBodySerializerPair, |
|
|
|
responseMediaType, |
|
|
|
resource, |
|
|
|
resourceId, |
|
|
|
}) => async (req: IncomingMessage, res: ServerResponse) => { |
|
|
|
if (method !== 'PUT') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const [, mainResourceRouteName, mainResourceId = ''] = url.split('/'); |
|
|
|
if (mainResourceId === '') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const theResource = Array.from(appState.resources).find((r) => r.routeName === mainResourceRouteName); |
|
|
|
if (typeof theResource === 'undefined') { |
|
|
|
return { |
|
|
|
handled: false |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const { deserializerPair: requestBodyDeserializerPair, encodingPair: requestBodyEncodingPair } = getDeserializerObjects(appState, req); |
|
|
|
if (typeof requestBodyDeserializerPair === 'undefined' || typeof requestBodyEncodingPair === 'undefined') { |
|
|
|
res.statusCode = constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE; |
|
|
@@ -452,7 +350,7 @@ export const handleEmplaceItem: Middleware = ({ |
|
|
|
|
|
|
|
let bodyDeserialized: unknown; |
|
|
|
try { |
|
|
|
const schema = theResource.schema.type === 'object' ? theResource.schema as v.ObjectSchema<any> : theResource.schema |
|
|
|
const schema = resource.schema.type === 'object' ? resource.schema as v.ObjectSchema<any> : resource.schema |
|
|
|
//console.log(schema); |
|
|
|
bodyDeserialized = await getBody( |
|
|
|
req, |
|
|
@@ -462,10 +360,10 @@ export const handleEmplaceItem: Middleware = ({ |
|
|
|
? v.merge([ |
|
|
|
schema as v.ObjectSchema<any>, |
|
|
|
v.object({ |
|
|
|
[theResource.idAttr]: v.transform( |
|
|
|
[resource.idAttr]: v.transform( |
|
|
|
v.any(), |
|
|
|
input => theResource.idSerializer(input), |
|
|
|
v.literal(mainResourceId) |
|
|
|
input => resource.idSerializer(input), |
|
|
|
v.literal(resourceId) |
|
|
|
) |
|
|
|
}) |
|
|
|
]) |
|
|
@@ -474,7 +372,7 @@ export const handleEmplaceItem: Middleware = ({ |
|
|
|
} catch (errRaw) { |
|
|
|
const err = errRaw as v.ValiError; |
|
|
|
res.statusCode = constants.HTTP_STATUS_BAD_REQUEST; |
|
|
|
res.statusMessage = `Invalid ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Invalid ${resource.itemName}`; |
|
|
|
|
|
|
|
if (Array.isArray(err.issues)) { |
|
|
|
// TODO better error reporting, localizable messages |
|
|
@@ -493,14 +391,14 @@ export const handleEmplaceItem: Middleware = ({ |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
await resource.dataSource.initialize(); |
|
|
|
const params = bodyDeserialized as Record<string, unknown>; |
|
|
|
const [newObject, isCreated] = await theResource.dataSource.emplace(mainResourceId, params); |
|
|
|
const [newObject, isCreated] = await resource.dataSource.emplace(resourceId, params); |
|
|
|
const theFormatted = responseBodySerializerPair.serialize(newObject); |
|
|
|
if (isCreated) { |
|
|
|
res.writeHead(constants.HTTP_STATUS_CREATED, { |
|
|
|
'Content-Type': responseMediaType, |
|
|
|
'Location': `${serverParams.baseUrl}/${theResource.routeName}/${mainResourceId}` |
|
|
|
'Location': `${serverParams.baseUrl}/${resource.routeName}/${resourceId}` |
|
|
|
}); |
|
|
|
} else { |
|
|
|
res.writeHead(constants.HTTP_STATUS_OK, { |
|
|
@@ -510,7 +408,7 @@ export const handleEmplaceItem: Middleware = ({ |
|
|
|
res.end(theFormatted); |
|
|
|
} catch { |
|
|
|
res.statusCode = constants.HTTP_STATUS_INTERNAL_SERVER_ERROR; |
|
|
|
res.statusMessage = `Could Not Return ${theResource.itemName}`; |
|
|
|
res.statusMessage = `Could Not Return ${resource.itemName}`; |
|
|
|
res.end(); |
|
|
|
} |
|
|
|
return { |
|
|
|