Skip to content

Commit

Permalink
Merge pull request #123 from ba-st/121-Language-content-negotiation
Browse files Browse the repository at this point in the history
Add language content negotiation support
  • Loading branch information
gcotelli authored Jun 15, 2021
2 parents da26b08 + c65699a commit c3c1883
Show file tree
Hide file tree
Showing 24 changed files with 773 additions and 140 deletions.
23 changes: 23 additions & 0 deletions docs/Controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,26 @@ example classes and their respective tests:
- `SouthAmericanCurrenciesRESTfulController` offers `/currencies` as an
_immutable_ resource, and `/currencies/<id>/banknotes` as an _immutable_
resource fresh for _365.000.000 seconds_.

## Language negotiation

For APIs requiring the support of several languages, Stargate offers support to
consider `Accept-Language` headers in the content negotiation. To enable
this, the request builder needs to be configured with the supported language tags.

```smalltalk
builder
addAsSupportedLanguage: 'en-US';
addAsSupportedLanguage: 'es-AR'
```

As soon as a language is supported, Stargate will consider the
`Accept-Language` header and make the negotiated language available in the
request context.

To access the negotiated language, send `targetLanguageTag` or
`withTargetLanguageTagDo:` to the request context. API implementors can then use
it to produce a response in the corresponding language.

Once language negotiation is enabled, the `Content-Language` header will
contain the negotiated language in the response.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ApplicationControlPlugin class >> endpoint [
{ #category : #'class initialization' }
ApplicationControlPlugin class >> initialize [

<ignoreForCoverage>
AvailableCommands := OrderedCollection new.
self
registerAsAvailableCommand:
Expand Down
60 changes: 32 additions & 28 deletions source/Stargate-Examples-Tests/PetStoreAPITest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,29 @@ PetStoreAPITest >> testCORSAllowingAnyOrigin [
| response |

api allowCrossOriginSharingApplying: [ :cors | cors allowAnyOrigin ].

response := self newClient
url: self baseUrl / 'pets' asUrl;
headerAt: 'Origin' put: self baseUrl printString;
headerAt: 'Access-Control-Request-Headers' put: 'Content-Type';
options;
response.
self
assert: response code equals: 204;

self
assert: response isNoContent;
assert: ( response headers at: 'Access-Control-Allow-Origin' ) equals: '*';
assert: ( response headers at: 'Access-Control-Allow-Headers' ) equals: 'Content-Type';
assert: ( response headers at: 'Vary' ) equals: 'Access-Control-Allow-Headers';
assert: ( response headers at: 'Access-Control-Allow-Methods' ) equals:'POST, GET'.
assert: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
assert: ( response headers at: 'Access-Control-Allow-Methods' ) equals: 'POST, GET'.

response := self newClient
url: self baseUrl / 'pets' asUrl;
headerAt: 'Origin' put: self baseUrl printString;
get;
response.

self
should: [ response headers at: 'Vary' ] raise: KeyNotFound;
deny: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
assert: ( response headers at: 'Access-Control-Allow-Origin' ) equals: '*'
]

Expand All @@ -93,10 +93,11 @@ PetStoreAPITest >> testCORSAllowingExactlyOneOrigin [
response.

self
assert: response code equals: 204;
assert: ( response headers at: 'Access-Control-Allow-Origin' ) asUrl equals: self baseUrl;
assert: response isNoContent;
assertUrl: ( response headers at: 'Access-Control-Allow-Origin' ) equals: self baseUrl;
assert: ( response headers at: 'Access-Control-Allow-Headers' ) equals: 'Content-Type';
assert: ( response headers at: 'Vary' ) equals: 'Access-Control-Allow-Headers';
assert: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
deny: response varyHeaderNames includes: 'Access-Control-Allow-Origin';
assert: ( response headers at: 'Access-Control-Allow-Methods' ) equals: 'POST, GET'.

response := self newClient
Expand All @@ -106,8 +107,9 @@ PetStoreAPITest >> testCORSAllowingExactlyOneOrigin [
response.

self
should: [ response headers at: 'Vary' ] raise: KeyNotFound;
assert: ( response headers at: 'Access-Control-Allow-Origin' ) asUrl equals: self baseUrl
deny: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
deny: response varyHeaderNames includes: 'Access-Control-Allow-Origin';
assertUrl: ( response headers at: 'Access-Control-Allow-Origin' ) equals: self baseUrl
]

{ #category : #'tests - CORS' }
Expand All @@ -131,21 +133,23 @@ PetStoreAPITest >> testCORSAllowingMoreThanOneOrigin [
response.

self
assert: response code equals: 204;
assert: ( response headers at: 'Access-Control-Allow-Origin' ) asUrl equals: self baseUrl;
assert: response isNoContent;
assertUrl: ( response headers at: 'Access-Control-Allow-Origin' ) equals: self baseUrl;
assert: ( response headers at: 'Access-Control-Allow-Headers' ) equals: 'Content-Type';
assert: ( response headers at: 'Vary' ) equals: 'Origin, Access-Control-Allow-Headers';
assert: response varyHeaderNames includes: 'Origin';
assert: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
assert: ( response headers at: 'Access-Control-Allow-Methods' ) equals: 'POST, GET'.

response := self newClient
url: self baseUrl / 'pets' asUrl;
headerAt: 'Origin' put: self baseUrl printString;
get;
response.

self
assert: ( response headers at: 'Vary' ) equals: 'Origin';
assert: ( response headers at: 'Access-Control-Allow-Origin' ) asUrl equals: self baseUrl
assert: response varyHeaderNames includes: 'Origin';
deny: response varyHeaderNames includes: 'Access-Control-Allow-Headers';
assertUrl: ( response headers at: 'Access-Control-Allow-Origin' ) equals: self baseUrl
]

{ #category : #'tests - CORS' }
Expand All @@ -170,15 +174,15 @@ PetStoreAPITest >> testCORSOptionalHeaders [
response.

self
assert: response code equals: 204;
assert: response isNoContent;
assert: ( response headers at: 'Access-Control-Allow-Origin' ) equals: '*';
assert: ( response headers at: 'Access-Control-Allow-Headers' ) equals: 'Content-Type';
assert: ( response headers at: 'Access-Control-Allow-Methods' ) equals: 'POST, GET';
assert: ( response headers at: 'Access-Control-Expose-Headers' )
equals: 'Authorization, X-Custom';
assert: ( response headers at: 'Access-Control-Allow-Credentials' ) equals: 'true';
assert: ( response headers at: 'Access-Control-Max-Age' ) equals: '600';
assert: ( response headers at: 'Vary' ) equals: 'Access-Control-Allow-Headers'.
assert: response varyHeaderNames includes: 'Access-Control-Allow-Headers'.

response := self newClient
url: self baseUrl / 'pets' asUrl;
Expand All @@ -194,16 +198,16 @@ PetStoreAPITest >> testCORSOptionalHeaders [
equals: 'Authorization, X-Custom';
assert: ( response headers at: 'Access-Control-Allow-Credentials' ) equals: 'true';
should: [ response headers at: 'Access-Control-Max-Age' ] raise: KeyNotFound;
should: [ response headers at: 'Vary' ] raise: KeyNotFound
deny: response varyHeaderNames includes: 'Access-Control-Allow-Headers'
]

{ #category : #'tests - CORS' }
PetStoreAPITest >> testCORSRespondWithEmptyHeadersIfOriginNotAllowed [

| response |

api allowCrossOriginSharingApplying: [ :cors | cors allowOnlyFrom: {'https://google.com' asUrl} ].
api allowCrossOriginSharingApplying: [ :cors | cors allowOnlyFrom: {'https://google.com' asUrl} ].

response := self newClient
url: self baseUrl / 'pets' asUrl;
headerAt: 'Origin' put: self baseUrl printString;
Expand All @@ -212,19 +216,19 @@ PetStoreAPITest >> testCORSRespondWithEmptyHeadersIfOriginNotAllowed [
response.

self
assert: response code equals: 204;
should: [ response headers at: 'Access-Control-Allow-Origin' ] raise: KeyNotFound;
assert: response isNoContent;
should: [ response headers at: 'Access-Control-Allow-Origin' ] raise: KeyNotFound;
should: [ response headers at: 'Access-Control-Allow-Headers' ] raise: KeyNotFound;
should: [ response headers at: 'Access-Control-Allow-Methods' ] raise: KeyNotFound.

response := self newClient
url: self baseUrl / 'pets' asUrl;
headerAt: 'Origin' put: self baseUrl printString;
get;
response.

self
should: [ response headers at: 'Vary' ] raise: KeyNotFound;
assert: response varyHeaderNames isEmpty;
should: [ response headers at: 'Access-Control-Allow-Origin' ] raise: KeyNotFound
]

Expand Down
Loading

0 comments on commit c3c1883

Please sign in to comment.