From cf38fc72815274baa523879fda87b17ca4d44b48 Mon Sep 17 00:00:00 2001 From: Juan Vanecek Date: Mon, 18 Apr 2022 09:42:49 -0300 Subject: [PATCH 1/3] enhance CachingDirectiveBuilder to apply directive depending on the response --- .../CachingDirectivesBuilder.class.st | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/source/Stargate-Model/CachingDirectivesBuilder.class.st b/source/Stargate-Model/CachingDirectivesBuilder.class.st index f6bd24c..c87ef90 100644 --- a/source/Stargate-Model/CachingDirectivesBuilder.class.st +++ b/source/Stargate-Model/CachingDirectivesBuilder.class.st @@ -5,7 +5,8 @@ Class { #name : #CachingDirectivesBuilder, #superclass : #Object, #instVars : [ - 'directives' + 'directives', + 'currentCondition' ], #category : #'Stargate-Model-Caching' } @@ -13,7 +14,11 @@ Class { { #category : #private } CachingDirectivesBuilder >> addCacheControlNamed: aName [ - directives add: [ :response :context | response addCachingDirective: aName ] + | condition | + + condition := currentCondition. + directives + add: [ :response :context | ( condition value: response ) then: [ response addCachingDirective: aName ] ] ] { #category : #private } @@ -28,6 +33,12 @@ CachingDirectivesBuilder >> addCacheControlNamed: aName withToken: aValue [ self addCacheControlNamed: ( '<1s>=<2p>' expandMacrosWith: aName with: aValue ) ] +{ #category : #private } +CachingDirectivesBuilder >> alwaysApplyDirectives [ + + currentCondition := [ :response | true ] +] + { #category : #configuring } CachingDirectivesBuilder >> beAvailableFor: aDuration [ @@ -119,10 +130,15 @@ CachingDirectivesBuilder >> doNotTransform [ { #category : #configuring } CachingDirectivesBuilder >> expireIn: aDuration [ + + | condition | + condition := currentCondition. directives - add: - [ :response :context | response headers at: 'Expires' put: ( ZnUtils httpDate: DateAndTime now + aDuration ) ] + add: [ :response :context | + ( condition value: response ) + then: [ response headers at: 'Expires' put: ( ZnUtils httpDate: DateAndTime now + aDuration ) ] + ] ] { #category : #private } @@ -141,9 +157,10 @@ CachingDirectivesBuilder >> fieldStringFor: aFieldNameCollection [ { #category : #initialization } CachingDirectivesBuilder >> initialize [ - + super initialize. - directives := OrderedCollection new + directives := OrderedCollection new. + self alwaysApplyDirectives ] { #category : #configuring } @@ -160,6 +177,15 @@ CachingDirectivesBuilder >> requireRevalidation [ beStaleAfter: 0 seconds ] +{ #category : #configuring } +CachingDirectivesBuilder >> when: aResponseCondition apply: aDirectiveConfiguration [ + + [ currentCondition := aResponseCondition. + aDirectiveConfiguration cull: self + ] + ensure: [ self alwaysApplyDirectives ] +] + { #category : #configuring } CachingDirectivesBuilder >> whenSharedBeStaleAfter: aDuration [ From 1a11c60de173dfb42aa4409964b38c65c97f8126 Mon Sep 17 00:00:00 2001 From: Juan Vanecek Date: Mon, 18 Apr 2022 09:43:20 -0300 Subject: [PATCH 2/3] use conditional caching directive in tests --- .../PetsRESTfulControllerTest.class.st | 45 +++++++++++++++++-- .../PetsRESTfulController.class.st | 16 +++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/source/Stargate-Examples-Tests/PetsRESTfulControllerTest.class.st b/source/Stargate-Examples-Tests/PetsRESTfulControllerTest.class.st index d1f00c9..5349c0c 100644 --- a/source/Stargate-Examples-Tests/PetsRESTfulControllerTest.class.st +++ b/source/Stargate-Examples-Tests/PetsRESTfulControllerTest.class.st @@ -365,7 +365,7 @@ PetsRESTfulControllerTest >> testGetPetSummaryJustCreated [ equals: resourceController petSummaryVersion1dot0dot0MediaType; assert: response entityTag equals: '"1323cdfb8bb0f9011fbd480c5a87c17d05883214"' asEntityTag. self assertExpiresHeaderFor: response with: 4 hours. - self assertCachingDirectivesFor: response with: #( 'public' 'no-transform' 'must-revalidate' ). + self assertCachingDirectivesFor: response with: #('public'). self withJsonFromContentsIn: response do: [ :json | self @@ -391,7 +391,7 @@ PetsRESTfulControllerTest >> testGetPets [ assert: response contentType asMediaType equals: resourceController petSummaryVersion1dot0dot0MediaType. self assertExpiresHeaderFor: response with: 4 hours. - self assertCachingDirectivesFor: response with: #('public' 'no-transform' 'must-revalidate'). + self assertCachingDirectivesFor: response with: #('public'). self withJsonFromContentsIn: response @@ -423,7 +423,7 @@ PetsRESTfulControllerTest >> testGetPetsNotEmpty [ assert: response contentType asMediaType equals: resourceController petSummaryVersion1dot0dot0MediaType. self assertExpiresHeaderFor: response with: 4 hours. - self assertCachingDirectivesFor: response with: #('public' 'no-transform' 'must-revalidate'). + self assertCachingDirectivesFor: response with: #('public'). self withJsonFromContentsIn: response @@ -441,6 +441,45 @@ PetsRESTfulControllerTest >> testGetPetsNotEmpty [ ] ] +{ #category : #'tests - get collection' } +PetsRESTfulControllerTest >> testGetPetsNotEmptyAcceptingCompleteMediaType [ + + | response | + + resourceController + createPetBasedOn: ( self requestToCreatePetFrom: '{"name":"Firulais","type":"dog"}' ) + within: self newHttpRequestContext. + + self assert: petRepository findAll notEmpty. + + response := resourceController + getPetsBasedOn: ( self requestToGetPetsAccepting: resourceController petVersion1dot0dot0MediaType ) + within: self newHttpRequestContext. + + self + assert: response isSuccess; + assert: response status equals: 200; + assert: response contentType asMediaType + equals: resourceController petVersion1dot0dot0MediaType. + self assertExpiresHeaderFor: response with: 4 hours. + self assertCachingDirectivesFor: response with: #('public' 'no-transform' 'must-revalidate'). + + self + withJsonFromContentsIn: response + do: [ :json | self assertUrl: json selfLocation equals: 'https://pets.example.com/pets' ]; + withJsonFromItemsIn: response + do: [ :items | + self + withTheOnlyOneIn: items + do: [ :dogSummary | + self + assert: dogSummary name equals: 'Firulais'; + assertUrl: dogSummary selfLocation equals: 'https://pets.example.com/pets/1'; + assert: dogSummary type equals: 'dog'. + ] + ] +] + { #category : #'tests - get collection' } PetsRESTfulControllerTest >> testGetPetsWithPagination [ diff --git a/source/Stargate-Examples/PetsRESTfulController.class.st b/source/Stargate-Examples/PetsRESTfulController.class.st index aa2d655..f51ebb0 100644 --- a/source/Stargate-Examples/PetsRESTfulController.class.st +++ b/source/Stargate-Examples/PetsRESTfulController.class.st @@ -203,10 +203,18 @@ PetsRESTfulController >> initializeRequestHandler [ ]; directCachingWith: [ :caching | caching - expireIn: 4 hours; - bePublic; - doNotTransform; - mustRevalidate + when: [ :response | response contentType = self petVersion1dot0dot0MediaType ] + apply: [ caching + expireIn: 4 hours; + bePublic; + doNotTransform; + mustRevalidate + ]; + when: [ :response | response contentType = self petSummaryVersion1dot0dot0MediaType ] + apply: [ caching + expireIn: 4 hours; + bePublic + ] ]; build ] From 8f59430c1ffa89641d686ff25480ed44e7d9fa9e Mon Sep 17 00:00:00 2001 From: Juan Vanecek Date: Mon, 18 Apr 2022 10:44:58 -0300 Subject: [PATCH 3/3] Update docs --- docs/reference/Controllers.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/reference/Controllers.md b/docs/reference/Controllers.md index 14e8cc5..34af5d0 100644 --- a/docs/reference/Controllers.md +++ b/docs/reference/Controllers.md @@ -263,11 +263,15 @@ Both `private` and `no-cache` directives can specify a list of fields that restrict them using `bePrivateRestrictedTo: aFieldNameCollection` and `doNotCacheRestrictedTo: aFieldNameCollection` respectively. +Additionally, if you may have different caching directive depending on the +response, you can add a condition before configuring the builder. + For specific examples on all the different options offered by Stargate, check the example classes and their respective tests: -- `PetsRESTfulController` offers `/pets` as a _public_ resource for _4 hours_, - which _can't be transformed_, and _must be revalidated_ once stale. +- `PetsRESTfulController` offers `/pets` as a _public_ resource for _4 hours_, but + when using a specific content-type it _can't be transformed_, and + _must be revalidated_ once stale. - `PetOrdersRESTfulController` offers `/orders` as a _public_ resource for _1 minute_, using both the `expires` header and the `max-age` directive. - `PetOrdersRESTfulController` offers `/orders//comments`, indicating