Skip to content

Commit

Permalink
Merge pull request #153 from ba-st/conditional-caching-directive
Browse files Browse the repository at this point in the history
Conditional caching directive
  • Loading branch information
gcotelli authored Apr 18, 2022
2 parents 13974c3 + 8f59430 commit 683620f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 15 deletions.
8 changes: 6 additions & 2 deletions docs/reference/Controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<id>/comments`, indicating
Expand Down
45 changes: 42 additions & 3 deletions source/Stargate-Examples-Tests/PetsRESTfulControllerTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 [

Expand Down
16 changes: 12 additions & 4 deletions source/Stargate-Examples/PetsRESTfulController.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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
]
Expand Down
38 changes: 32 additions & 6 deletions source/Stargate-Model/CachingDirectivesBuilder.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ Class {
#name : #CachingDirectivesBuilder,
#superclass : #Object,
#instVars : [
'directives'
'directives',
'currentCondition'
],
#category : #'Stargate-Model-Caching'
}

{ #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 }
Expand All @@ -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 [

Expand Down Expand Up @@ -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 }
Expand All @@ -141,9 +157,10 @@ CachingDirectivesBuilder >> fieldStringFor: aFieldNameCollection [

{ #category : #initialization }
CachingDirectivesBuilder >> initialize [

super initialize.
directives := OrderedCollection new
directives := OrderedCollection new.
self alwaysApplyDirectives
]

{ #category : #configuring }
Expand All @@ -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 [

Expand Down

0 comments on commit 683620f

Please sign in to comment.