import express from 'express' ; import { uuid , pathsFor , AlreadyPublishedError , BadParamsError , NotFoundError , ValidationError , ServerError , isUUID , DependencyMissingError , ConflictError } from '@mds-core/mds-utils' ; import db from '@mds-core/mds-db' ; import { policyValidationDetails } from '@mds-core/mds-schema-validators' ; import logger from '@mds-core/mds-logger' ; import { checkAccess , AccessTokenScopeValidator , ApiRequest , ApiResponse } from '@mds-core/mds-api-server' ; import { PolicyAuthorApiVersionMiddleware } from './middleware/policy-author-api-version' ; import { PolicyAuthorApiRequest , PostPolicyResponse , EditPolicyResponse , DeletePolicyResponse , PolicyAuthorApiAccessTokenScopes , PublishPolicyResponse , GetPolicyMetadataResponse , GetPolicyMetadatumResponse , EditPolicyMetadataResponse } from './types' ; const checkPolicyAuthorApiAccess = ( validator ) => checkAccess ( validator ) ; function api ( app ) { app . use ( PolicyAuthorApiVersionMiddleware ) ; app . post ( pathsFor ( '/policies' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:write' ) ) , async ( req , res , next ) => { const policy = { policy_id : uuid ( ) , ... req . body } ; const details = policyValidationDetails ( policy ) ; if ( details != null ) { return res . status ( 400 ) . send ( { error : new ValidationError ( JSON . stringify ( details ) ) } ) ; } try { await db . writePolicy ( policy ) ; return res . status ( 201 ) . send ( { version : res . locals . version , data : { policy } } ) ; } catch ( error ) { if ( error . code === '23505' ) { return res . status ( 409 ) . send ( { error : new ConflictError ( `policy ${ policy . policy_id } already exists! Did you mean to PUT?` ) } ) ; } return next ( new ServerError ( error ) ) ; } } ) ; app . post ( pathsFor ( '/policies/:policy_id/publish' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:publish' ) ) , async ( req , res , next ) => { const { policy_id } = req . params ; try { const policy = await db . publishPolicy ( policy_id ) ; return res . status ( 200 ) . send ( { version : res . locals . version , data : { policy } } ) ; } catch ( error ) { logger . error ( 'failed to publish policy' , error . stack ) ; if ( error instanceof AlreadyPublishedError ) { return res . status ( 409 ) . send ( { error } ) ; } if ( error instanceof NotFoundError ) { return res . status ( 404 ) . send ( { error } ) ; } if ( error instanceof DependencyMissingError ) { return res . status ( 424 ) . send ( { error } ) ; } return next ( new ServerError ( error ) ) ; } } ) ; app . put ( pathsFor ( '/policies/:policy_id' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:write' ) ) , async ( req , res , next ) => { const policy = req . body ; const details = policyValidationDetails ( policy ) ; if ( details !== null ) { return res . status ( 400 ) . send ( { error : new ValidationError ( JSON . stringify ( details ) ) } ) ; } try { await db . editPolicy ( policy ) ; const result = await db . readPolicy ( policy . policy_id ) ; return res . status ( 200 ) . send ( { version : res . locals . version , data : { policy : result } } ) ; } catch ( error ) { if ( error instanceof NotFoundError ) { return res . status ( 404 ) . send ( { error } ) ; } if ( error instanceof AlreadyPublishedError ) { return res . status ( 409 ) . send ( { error } ) ; } return next ( new ServerError ( error ) ) ; } } ) ; app . delete ( pathsFor ( '/policies/:policy_id' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:delete' ) ) , async ( req , res , next ) => { const { policy_id } = req . params ; try { await db . deletePolicy ( policy_id ) ; return res . status ( 200 ) . send ( { version : res . locals . version , data : { policy_id } } ) ; } catch ( error ) { logger . error ( 'failed to delete policy' , error . stack ) ; return res . status ( 404 ) . send ( { error } ) ; } } ) ; app . get ( pathsFor ( '/policies/meta/' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:read' ) ) , async ( req , res , next ) => { const { get_published , get_unpublished } = req . query ; const params = { get_published : get_published ? get_published === 'true' : null , get_unpublished : get_unpublished ? get_unpublished === 'true' : null } ; try { const policy_metadata = await db . readBulkPolicyMetadata ( params ) ; if ( policy_metadata . length === 0 ) { throw new NotFoundError ( 'No metadata found' ) ; } res . status ( 200 ) . send ( { version : res . locals . version , data : { policy_metadata } } ) ; } catch ( error ) { if ( error instanceof BadParamsError ) { res . status ( 400 ) . send ( { error : 'Cannot set both get_unpublished and get_published to be true. If you want all policy metadata, set both params to false or do not send them.' } ) ; } if ( error instanceof NotFoundError ) { res . status ( 404 ) . send ( { error } ) ; } return next ( new ServerError ( error ) ) ; } } ) ; app . get ( pathsFor ( '/policies/:policy_id/meta' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:read' ) ) , async ( req , res , next ) => { const { policy_id } = req . params ; try { if ( ! isUUID ( policy_id ) ) { throw new BadParamsError ( `${ policy_id } is not a valid UUID` ) ; } const result = await db . readSinglePolicyMetadata ( policy_id ) ; return res . status ( 200 ) . send ( { version : res . locals . version , data : { policy_metadata : result } } ) ; } catch ( error ) { if ( error instanceof NotFoundError ) { return res . status ( 404 ) . send ( { error } ) ; } if ( error instanceof BadParamsError ) { return res . status ( 400 ) . send ( { error } ) ; } return next ( new ServerError ( error ) ) ; } } ) ; app . put ( pathsFor ( '/policies/:policy_id/meta' ) , checkPolicyAuthorApiAccess ( scopes => scopes . includes ( 'policies:write' ) ) , async ( req , res , next ) => { const policy_metadata = req . body ; try { await db . updatePolicyMetadata ( policy_metadata ) ; return res . status ( 200 ) . send ( { version : res . locals . version , data : { policy_metadata } } ) ; } catch ( updateErr ) { if ( updateErr instanceof NotFoundError ) { try { await db . writePolicyMetadata ( policy_metadata ) ; return res . status ( 201 ) . send ( { version : res . locals . version , data : { policy_metadata } } ) ; } catch ( writeErr ) { return next ( new ServerError ( writeErr ) ) ; } } return next ( new ServerError ( updateErr ) ) ; } } ) ; app . use ( async ( error , req , res ) => { await logger . error ( req . method , req . originalUrl , error ) ; return res . status ( 500 ) . send ( { error } ) ; } ) ; return app ; } export { api } ;