|
@@ -1,7 +1,7 @@ |
|
|
import { constants } from 'http2'; |
|
|
import { constants } from 'http2'; |
|
|
import Negotiator from 'negotiator'; |
|
|
import Negotiator from 'negotiator'; |
|
|
import { ValiError } from 'valibot'; |
|
|
import { ValiError } from 'valibot'; |
|
|
import { Middleware } from './core'; |
|
|
|
|
|
|
|
|
import {Middleware} from './core'; |
|
|
import { getBody, getMethod, getUrl } from './utils'; |
|
|
import { getBody, getMethod, getUrl } from './utils'; |
|
|
|
|
|
|
|
|
export const handleHasMethodAndUrl: Middleware = ({}) => (req, res) => { |
|
|
export const handleHasMethodAndUrl: Middleware = ({}) => (req, res) => { |
|
@@ -220,6 +220,165 @@ export const handleGetItem: Middleware = ({ |
|
|
}; |
|
|
}; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const handleDeleteItem: Middleware = ({ |
|
|
|
|
|
appState, |
|
|
|
|
|
serverParams |
|
|
|
|
|
}) => async (req, res) => { |
|
|
|
|
|
const method = getMethod(req); |
|
|
|
|
|
if (method !== 'DELETE') { |
|
|
|
|
|
return { |
|
|
|
|
|
handled: false |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const baseUrl = serverParams.baseUrl ?? ''; |
|
|
|
|
|
const { url } = getUrl(req, baseUrl); |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (theResource.checksSerializersOnDelete) { |
|
|
|
|
|
const negotiator = new Negotiator(req); |
|
|
|
|
|
const availableMediaTypes = Array.from(appState.serializers.keys()); |
|
|
|
|
|
const theMediaType = negotiator.mediaType(availableMediaTypes); |
|
|
|
|
|
if (typeof theMediaType === 'undefined') { |
|
|
|
|
|
res.statusCode = constants.HTTP_STATUS_NOT_ACCEPTABLE; |
|
|
|
|
|
res.end(); |
|
|
|
|
|
return { |
|
|
|
|
|
handled: true |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const theSerializerPair = appState.serializers.get(theMediaType); |
|
|
|
|
|
if (typeof theSerializerPair === 'undefined') { |
|
|
|
|
|
res.statusCode = constants.HTTP_STATUS_NOT_ACCEPTABLE; |
|
|
|
|
|
res.end(); |
|
|
|
|
|
return { |
|
|
|
|
|
handled: true |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
await theResource.dataSource.initialize(); |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await theResource.dataSource.delete(mainResourceId); |
|
|
|
|
|
if (typeof response !== 'undefined' && !response && theResource.throws404OnDeletingNotFound) { |
|
|
|
|
|
res.statusCode = constants.HTTP_STATUS_NOT_FOUND; |
|
|
|
|
|
res.statusMessage = `${theResource.itemName} Not Found`; |
|
|
|
|
|
} else { |
|
|
|
|
|
res.statusCode = constants.HTTP_STATUS_NO_CONTENT; |
|
|
|
|
|
} |
|
|
|
|
|
res.end(); |
|
|
|
|
|
return { |
|
|
|
|
|
handled: true |
|
|
|
|
|
}; |
|
|
|
|
|
} catch { |
|
|
|
|
|
// TODO error handling |
|
|
|
|
|
// what if item is already deleted? Should we hide it by returning no content or throw a 404? |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
res.statusCode = constants.HTTP_STATUS_INTERNAL_SERVER_ERROR; |
|
|
|
|
|
res.end(); |
|
|
|
|
|
return { |
|
|
|
|
|
handled: true |
|
|
|
|
|
}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// export const handlePatchItem: Middleware = ({ |
|
|
|
|
|
// appState, |
|
|
|
|
|
// serverParams, |
|
|
|
|
|
// }) => async (req, res) => { |
|
|
|
|
|
// const method = getMethod(req); |
|
|
|
|
|
// if (method !== 'PATCH') { |
|
|
|
|
|
// return { |
|
|
|
|
|
// handled: false |
|
|
|
|
|
// }; |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// const baseUrl = serverParams.baseUrl ?? ''; |
|
|
|
|
|
// const { url } = getUrl(req, baseUrl); |
|
|
|
|
|
// |
|
|
|
|
|
// 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 theDeserializer = appState.serializers.get(req.headers['content-type'] ?? 'application/octet-stream'); |
|
|
|
|
|
// if (typeof theDeserializer === 'undefined') { |
|
|
|
|
|
// res.statusCode = constants.HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE; |
|
|
|
|
|
// res.end(); |
|
|
|
|
|
// return { |
|
|
|
|
|
// handled: true |
|
|
|
|
|
// }; |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// const negotiator = new Negotiator(req); |
|
|
|
|
|
// const availableMediaTypes = Array.from(appState.serializers.keys()); |
|
|
|
|
|
// const theMediaType = negotiator.mediaType(availableMediaTypes); |
|
|
|
|
|
// if (typeof theMediaType === 'undefined') { |
|
|
|
|
|
// res.statusCode = constants.HTTP_STATUS_NOT_ACCEPTABLE; |
|
|
|
|
|
// res.end(); |
|
|
|
|
|
// return { |
|
|
|
|
|
// handled: true |
|
|
|
|
|
// }; |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// const theSerializerPair = appState.serializers.get(theMediaType); |
|
|
|
|
|
// if (typeof theSerializerPair === 'undefined') { |
|
|
|
|
|
// res.statusCode = constants.HTTP_STATUS_NOT_ACCEPTABLE; |
|
|
|
|
|
// res.end(); |
|
|
|
|
|
// return { |
|
|
|
|
|
// handled: true |
|
|
|
|
|
// }; |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// await theResource.dataSource.initialize(); |
|
|
|
|
|
// let bodyDeserialized: unknown; |
|
|
|
|
|
// try { |
|
|
|
|
|
// bodyDeserialized = await getBody(req, theDeserializer, theResource.schema); |
|
|
|
|
|
// } catch (errRaw) { |
|
|
|
|
|
// const err = errRaw as ValiError; |
|
|
|
|
|
// res.statusCode = constants.HTTP_STATUS_BAD_REQUEST; |
|
|
|
|
|
// res.statusMessage = `Invalid ${theResource.itemName}`; |
|
|
|
|
|
// |
|
|
|
|
|
// if (Array.isArray(err.issues)) { |
|
|
|
|
|
// // TODO better error reporting, localizable messages |
|
|
|
|
|
// const theFormatted = theSerializerPair.serialize( |
|
|
|
|
|
// err.issues.map((i) => ( |
|
|
|
|
|
// `${i.path?.map((p) => p.key)?.join('.') ?? i.reason}: ${i.message}` |
|
|
|
|
|
// )) |
|
|
|
|
|
// ); |
|
|
|
|
|
// res.end(theFormatted); |
|
|
|
|
|
// } else { |
|
|
|
|
|
// res.end(); |
|
|
|
|
|
// } |
|
|
|
|
|
// return { |
|
|
|
|
|
// handled: true |
|
|
|
|
|
// }; |
|
|
|
|
|
// } |
|
|
|
|
|
// }; |
|
|
|
|
|
|
|
|
export const handleCreateItem: Middleware = ({ |
|
|
export const handleCreateItem: Middleware = ({ |
|
|
appState, |
|
|
appState, |
|
|
serverParams |
|
|
serverParams |
|
|