@energica-city/shared-amplify-utils
Version:
Shared utilities for AWS Amplify projects
267 lines • 10.2 kB
JavaScript
import { RestErrors } from '../middleware/rest/RestErrors';
import { getErrorMessage, isNotFoundError, isValidationError, isConflictError, } from './helpers';
/**
* Creates REST-aware query factory wrappers with automatic HTTP error mapping.
*
* Wraps standard QueryFactory operations to intercept database errors and convert
* them to appropriate REST errors with proper HTTP status codes. This enables
* consistent REST API error responses without manual error handling in each endpoint.
*
* **Error Mapping Strategy:**
* - `"No data returned"` → 404 Not Found
* - AppSync validation errors → 400 Bad Request
* - Conflict/constraint violations → 409 Conflict
* - All other errors → 500 Internal Server Error
*
* @template T - Model name type extending string
* @template TTypes - Record of all available Amplify model types
* @param rawModel - Original QueryFactory instance to wrap
* @param modelName - Human-readable model name for error messages
* @param context - Request context object for error logging and tracing
* @returns REST-aware query operations that throw HTTP-appropriate errors
*
* @example
* ```typescript
* const restQueries = createRestAwareQueryOperations(
* queries.User,
* "User",
* { requestId: "req-123", userId: "current-user" }
* );
*
* // Automatically throws 404 RestError if user not found
* const user = await restQueries.get({ input: { userId: "123" } });
* ```
*/
export function createRestAwareQueryOperations(rawModel, modelName, context) {
return {
get: createRestAwareGetOperation(rawModel, modelName, context),
create: createRestAwareCreateOperation(rawModel, modelName, context),
update: createRestAwareUpdateOperation(rawModel, modelName, context),
delete: createRestAwareDeleteOperation(rawModel, modelName, context),
list: createRestAwareListOperation(rawModel, modelName, context),
queryIndex: createRestAwareQueryIndexOperation(rawModel, modelName, context),
};
}
/**
* Internal factory functions that create REST-aware operation handlers.
* These functions maintain type information and provide consistent error handling patterns.
*/
/**
* Creates a REST-aware get operation with 404 error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs get operation with REST error handling
*/
function createRestAwareGetOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.get(props);
}
catch (error) {
const message = getErrorMessage(error);
if (isNotFoundError(message)) {
throw RestErrors.notFound(`${modelName} not found`, {
...context,
modelName,
searchCriteria: props.input,
}, error);
}
else {
throw RestErrors.internal(`Failed to retrieve ${modelName}`, {
...context,
modelName,
operation: 'get',
searchCriteria: props.input,
}, error);
}
}
};
}
/**
* Creates a REST-aware create operation with validation and conflict error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs create operation with REST error handling
*/
function createRestAwareCreateOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.create(props);
}
catch (error) {
const message = getErrorMessage(error);
if (isValidationError(message)) {
throw RestErrors.validation(`Invalid data for ${modelName} creation`, {
...context,
modelName,
inputData: props.input,
}, error);
}
else if (isConflictError(message)) {
throw RestErrors.conflict(`${modelName} already exists or conflicts with existing data`, {
...context,
modelName,
inputData: props.input,
}, error);
}
else {
throw RestErrors.internal(`Failed to create ${modelName}`, {
...context,
modelName,
operation: 'create',
inputData: props.input,
}, error);
}
}
};
}
/**
* Creates a REST-aware update operation with comprehensive error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs update operation with REST error handling
*/
function createRestAwareUpdateOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.update(props);
}
catch (error) {
const message = getErrorMessage(error);
if (isNotFoundError(message)) {
throw RestErrors.notFound(`${modelName} not found for update`, {
...context,
modelName,
updateCriteria: props.input,
}, error);
}
else if (isValidationError(message)) {
throw RestErrors.validation(`Invalid data for ${modelName} update`, {
...context,
modelName,
updateData: props.input,
}, error);
}
else if (isConflictError(message)) {
throw RestErrors.conflict(`Update conflicts with existing ${modelName} data`, {
...context,
modelName,
updateData: props.input,
}, error);
}
else {
throw RestErrors.internal(`Failed to update ${modelName}`, {
...context,
modelName,
operation: 'update',
updateData: props.input,
}, error);
}
}
};
}
/**
* Creates a REST-aware delete operation with 404 error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs delete operation with REST error handling
*/
function createRestAwareDeleteOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.delete(props);
}
catch (error) {
const message = getErrorMessage(error);
if (isNotFoundError(message)) {
throw RestErrors.notFound(`${modelName} not found for deletion`, {
...context,
modelName,
deleteCriteria: props.input,
}, error);
}
else {
throw RestErrors.internal(`Failed to delete ${modelName}`, {
...context,
modelName,
operation: 'delete',
deleteCriteria: props.input,
}, error);
}
}
};
}
/**
* Creates a REST-aware list operation with general error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs list operation with REST error handling
*/
function createRestAwareListOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.list(props);
}
catch (error) {
throw RestErrors.internal(`Failed to list ${modelName} items`, {
...context,
modelName,
operation: 'list',
listParams: props,
}, error);
}
};
}
/**
* Creates a REST-aware queryIndex operation with general error handling.
*
* @internal
* @template TTypes - Record of all available Amplify model types
* @template T - Model name as string literal
* @param rawModel - Original QueryFactory instance
* @param modelName - Human-readable model name for error messages
* @param context - Request context for error logging
* @returns Function that performs queryIndex operation with REST error handling
*/
function createRestAwareQueryIndexOperation(rawModel, modelName, context) {
return async (props) => {
try {
return await rawModel.queryIndex(props);
}
catch (error) {
throw RestErrors.internal(`Failed to query ${modelName} index: ${props.queryField}`, {
...context,
modelName,
operation: 'queryIndex',
queryField: props.queryField,
indexParams: props,
}, error);
}
};
}
//# sourceMappingURL=restAware.js.map