From b608853c33c49e66e7864e32d4dd5dca84dbce74 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren <christer@infotech.no> Date: Tue, 19 Dec 2023 00:34:58 +0100 Subject: [PATCH] Add Location header to POST response even if resource link is skipped --- src/Felicity/Operations.fs | 23 ++++++--------------- src/Felicity/Relationships.fs | 2 +- src/Felicity/ResourceBuilder.fs | 14 ++++++++----- src/Felicity/ResponseBuilder.fs | 3 ++- src/Felicity/RoutingOperations.fs | 34 ++++++++++++++++++------------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Felicity/Operations.fs b/src/Felicity/Operations.fs index 51def95..4dbabcf 100644 --- a/src/Felicity/Operations.fs +++ b/src/Felicity/Operations.fs @@ -604,17 +604,11 @@ type PostOperation<'originalCtx, 'ctx, 'entity> = internal { return! handler next httpCtx else - let! doc = resp.Write httpCtx ctx req (rDef, entity1) + let! doc, selfUrlOpt = resp.Write httpCtx ctx req (rDef, entity1) let setLocationHeader = - match doc with - | { - data = Some { links = Include links } - } -> - match links.TryGetValue "self" with - | true, { href = Some url } -> - setHttpHeader "Location" (url.ToString()) - | _ -> fun next ctx -> next ctx + match selfUrlOpt with + | Some url -> setHttpHeader "Location" (url.ToString()) | _ -> fun next ctx -> next ctx let! fieldTrackerHandler = @@ -832,16 +826,11 @@ type PostCustomHelper<'ctx, 'entity> member this.ReturnCreatedEntity(entity: 'entity) : HttpHandler = fun next httpCtx -> task { - let! doc = builder.Write httpCtx ctx req (rDef, entity) + let! doc, selfUrlOpt = builder.Write httpCtx ctx req (rDef, entity) let setLocationHeader = - match doc with - | { - data = Some { links = Include links } - } -> - match links.TryGetValue "self" with - | true, { href = Some url } -> setHttpHeader "Location" (url.ToString()) - | _ -> fun next ctx -> next ctx + match selfUrlOpt with + | Some url -> setHttpHeader "Location" (url.ToString()) | _ -> fun next ctx -> next ctx let! fieldTrackerHandler = diff --git a/src/Felicity/Relationships.fs b/src/Felicity/Relationships.fs index 839a41b..bdc5882 100644 --- a/src/Felicity/Relationships.fs +++ b/src/Felicity/Relationships.fs @@ -34,7 +34,7 @@ module private RelationshipHelpers = } resp.Write httpCtx ctx reqForIncluded (parentResDef, parentEntity) - |> Task.map (fun doc -> doc.included) + |> Task.map (fun (doc, _) -> doc.included) diff --git a/src/Felicity/ResourceBuilder.fs b/src/Felicity/ResourceBuilder.fs index 791c2ad..830771e 100644 --- a/src/Felicity/ResourceBuilder.fs +++ b/src/Felicity/ResourceBuilder.fs @@ -10,6 +10,14 @@ let private emptyMetaDictNeverModify = Dictionary(0) let emptyLinkArrayNeverModify = [||] +let getSelfUrlOpt<'ctx> resourceModule baseUrl (resDef: ResourceDefinition<'ctx>) resId = + if ResourceModule.hasGetResourceOperation<'ctx> resourceModule then + resDef.CollectionName + |> Option.map (fun collName -> baseUrl + "/" + collName + "/" + resId) + else + None + + type ResourceBuilder<'ctx> ( resourceModuleMap: Map<ResourceTypeName, Type>, @@ -51,11 +59,7 @@ type ResourceBuilder<'ctx> $"Framework bug: Attempted to build resource '%s{resourceDef.TypeName}', but no resource module was found" let selfUrlOpt = - if ResourceModule.hasGetResourceOperation<'ctx> resourceModule then - resourceDef.CollectionName - |> Option.map (fun collName -> baseUrl + "/" + collName + "/" + identifier.id) - else - None + getSelfUrlOpt<'ctx> resourceModule baseUrl resourceDef identifier.id let constrainedFields = ResourceModule.constrainedFields<'ctx> resourceModule diff --git a/src/Felicity/ResponseBuilder.fs b/src/Felicity/ResponseBuilder.fs index 0d58177..b00c0df 100644 --- a/src/Felicity/ResponseBuilder.fs +++ b/src/Felicity/ResponseBuilder.fs @@ -4,7 +4,8 @@ open System.Threading.Tasks open Microsoft.AspNetCore.Http type internal ResponseBuilder<'ctx> = - abstract Write: HttpContext -> 'ctx -> Request -> (ResourceDefinition<'ctx> * 'entity) -> Task<ResourceDocument> + abstract Write: + HttpContext -> 'ctx -> Request -> (ResourceDefinition<'ctx> * 'entity) -> Task<ResourceDocument * string option> abstract WriteList: HttpContext -> 'ctx -> Request -> (ResourceDefinition<'ctx> * 'entity) list -> Task<ResourceCollectionDocument> diff --git a/src/Felicity/RoutingOperations.fs b/src/Felicity/RoutingOperations.fs index ef68835..fa02ce4 100644 --- a/src/Felicity/RoutingOperations.fs +++ b/src/Felicity/RoutingOperations.fs @@ -114,20 +114,26 @@ module internal RoutingOperations = ) |> ResourceBuilder.buildOne (httpCtx.GetService<ILoggerFactory>()) - return { - ResourceDocument.jsonapi = Skip // support later when valid use-cases arrive - links = Skip // support later when valid use-cases arrive; remember to check LinkConfig - meta = - httpCtx.GetService<MetaGetter<'ctx>>().GetMeta ctx - |> Include - |> Skippable.filter (fun x -> x.Count > 0) - data = Some main - included = - if req.Query.ContainsKey "include" then - Include included - else - Skip - } + return + { + ResourceDocument.jsonapi = Skip // support later when valid use-cases arrive + links = Skip // support later when valid use-cases arrive; remember to check LinkConfig + meta = + httpCtx.GetService<MetaGetter<'ctx>>().GetMeta ctx + |> Include + |> Skippable.filter (fun x -> x.Count > 0) + data = Some main + included = + if req.Query.ContainsKey "include" then + Include included + else + Skip + }, + ResourceBuilder.getSelfUrlOpt<'ctx> + resourceModuleMap[resourceDef.TypeName] + baseUrl + resourceDef + (resourceDef.GetIdBoxed e) } member _.WriteList httpCtx ctx req rDefsEntities =