diff --git a/source/BaselineOfOpenAPI/BaselineOfOpenAPI.class.st b/source/BaselineOfOpenAPI/BaselineOfOpenAPI.class.st index b4f9039..6a033d1 100644 --- a/source/BaselineOfOpenAPI/BaselineOfOpenAPI.class.st +++ b/source/BaselineOfOpenAPI/BaselineOfOpenAPI.class.st @@ -16,15 +16,14 @@ BaselineOfOpenAPI >> baseline: spec [ package: 'OpenAPI-Core-Tests' with: [ spec requires: #('OpenAPI-Core' 'JSONSchema Tests'). ]; package: 'OpenAPI-REST' with: [ spec requires: #('OpenAPI-Core' 'ZincHTTPComponents') ]; package: 'OpenAPI-REST-Tests' with: [ spec requires: #('OpenAPI-REST') ]; - package: 'OpenAPI-Client' with: [ spec requires: #('OpenAPI-Core') ]. - + package: 'OpenAPI-Client' with: [ spec requires: #('OpenAPI-Core') ]; + package: 'OpenAPI-Client-Tests' with: [ spec requires: #('OpenAPI-Client') ]. spec group: 'default' with: #('Core' 'REST' 'Client' 'Tests'); group: 'Core' with: #('OpenAPI-Core'); group: 'REST' with: #('Core' 'OpenAPI-REST'); group: 'Client' with: #('Core' 'OpenAPI-Client'); - group: 'Tests' with: #('OpenAPI-Core-Tests' 'OpenAPI-REST-Tests'). - + group: 'Tests' with: #('OpenAPI-Core-Tests' 'OpenAPI-REST-Tests' 'OpenAPI-Client-Tests'). self neoJson: spec; zinHTTPComponents: spec; diff --git a/source/OpenAPI-Client-Tests/OARequestBuilderTest.class.st b/source/OpenAPI-Client-Tests/OARequestBuilderTest.class.st new file mode 100644 index 0000000..3ae1123 --- /dev/null +++ b/source/OpenAPI-Client-Tests/OARequestBuilderTest.class.st @@ -0,0 +1,78 @@ +Class { + #name : 'OARequestBuilderTest', + #superclass : 'TestCase', + #category : 'OpenAPI-Client-Tests-Tests', + #package : 'OpenAPI-Client-Tests', + #tag : 'Tests' +} + +{ #category : 'tests' } +OARequestBuilderTest >> testAddFormBodyBuildsFormUrlEncodedEntity [ + | client builder entity | + client := ZnClient new. + builder := OARequestBuilder new client: client. + builder addFormBody: (Dictionary new at: 'email' put: 'test@example.com'; yourself). + entity := client request entity. + self assert: entity contentType sub equals: 'x-www-form-urlencoded'. + self assert: (entity contents at: 'email') equals: 'test@example.com' +] + +{ #category : 'tests' } +OARequestBuilderTest >> testFlattenArrayOfObjects [ + | builder result items | + items := OrderedCollection new + add: (Dictionary new at: 'price' put: 'price_abc'; at: 'quantity' put: '1'; yourself); + yourself. + builder := OARequestBuilder new. + result := builder flattenFormParameters: (Dictionary new at: 'items' put: items; yourself). + self assert: (result at: 'items[0][price]') equals: 'price_abc'. + self assert: (result at: 'items[0][quantity]') equals: '1' +] + +{ #category : 'tests' } +OARequestBuilderTest >> testFlattenDeeplyNestedObject [ + | builder result nested | + nested := Dictionary new + at: 'end_behavior' put: (Dictionary new at: 'missing_payment_method' put: 'cancel'; yourself); + yourself. + builder := OARequestBuilder new. + result := builder flattenFormParameters: (Dictionary new at: 'trial_settings' put: nested; yourself). + self assert: (result at: 'trial_settings[end_behavior][missing_payment_method]') equals: 'cancel' +] + +{ #category : 'tests' } +OARequestBuilderTest >> testFlattenNestedDictionary [ + | builder result | + builder := OARequestBuilder new. + result := builder flattenFormParameters: (Dictionary new + at: 'metadata' put: (Dictionary new at: 'apptiveid' put: 'acc_42'; yourself); + yourself). + self assert: (result at: 'metadata[apptiveid]') equals: 'acc_42' +] + +{ #category : 'tests' } +OARequestBuilderTest >> testFlattenNestedOrderedDictionary [ + "NeoJSON-produced objects (NeoJSONObject/OrderedDictionary) must flatten the + same way as plain Dictionary - this is what schema-validated request bodies + actually are, not a hand-built Dictionary." + | builder result neoObject | + neoObject := NeoJSONObject new. + neoObject at: 'apptiveid' put: 'acc_42'. + builder := OARequestBuilder new. + result := builder flattenFormParameters: (Dictionary new + at: 'metadata' put: neoObject; + yourself). + self assert: (result at: 'metadata[apptiveid]') equals: 'acc_42' +] + +{ #category : 'tests' } +OARequestBuilderTest >> testFlattenScalarValues [ + | builder result | + builder := OARequestBuilder new. + result := builder flattenFormParameters: (Dictionary new + at: 'customer' put: 'cus_123'; + at: 'trial_end' put: 'now'; + yourself). + self assert: (result at: 'customer') equals: 'cus_123'. + self assert: (result at: 'trial_end') equals: 'now' +] diff --git a/source/OpenAPI-Client-Tests/package.st b/source/OpenAPI-Client-Tests/package.st new file mode 100644 index 0000000..83e6c8f --- /dev/null +++ b/source/OpenAPI-Client-Tests/package.st @@ -0,0 +1 @@ +Package { #name : 'OpenAPI-Client-Tests' } diff --git a/source/OpenAPI-Client/OARequestBuilder.class.st b/source/OpenAPI-Client/OARequestBuilder.class.st index 758ed29..5945dbf 100644 --- a/source/OpenAPI-Client/OARequestBuilder.class.st +++ b/source/OpenAPI-Client/OARequestBuilder.class.st @@ -76,7 +76,7 @@ OARequestBuilder >> flattenFormParameters: anObject prefix: aPrefixString into: anObject isString ifTrue: [ aDictionary at: aPrefixString put: anObject. ^ self ]. - (anObject isKindOf: Dictionary) ifTrue: [ + ((anObject isKindOf: Dictionary) or: [ anObject isKindOf: OrderedDictionary ]) ifTrue: [ anObject keysAndValuesDo: [ :k :v | self flattenFormParameters: v diff --git a/source/OpenAPI-Client/OpenApiClient.class.st b/source/OpenAPI-Client/OpenApiClient.class.st index 580b493..150c8fc 100644 --- a/source/OpenAPI-Client/OpenApiClient.class.st +++ b/source/OpenAPI-Client/OpenApiClient.class.st @@ -33,6 +33,11 @@ OpenApiClient >> call: aString withArguments: aCollection [ ] +{ #category : 'requests' } +OpenApiClient >> handleError: anObject request: aRequest response: aResponse [ + ^ self handleError: anObject response: aResponse +] + { #category : 'as yet unclassified' } OpenApiClient >> handleError: anObject response: aResponse [ (OAUnspecifiedError new diff --git a/source/OpenAPI-Core/JSONSchemaAnyObject.extension.st b/source/OpenAPI-Core/JSONSchemaAnyObject.extension.st new file mode 100644 index 0000000..176ce5c --- /dev/null +++ b/source/OpenAPI-Core/JSONSchemaAnyObject.extension.st @@ -0,0 +1,6 @@ +Extension { #name : 'JSONSchemaAnyObject' } + +{ #category : '*OpenAPI-Core' } +JSONSchemaAnyObject >> acceptOpenApi: aVisitor [ + ^ self +] diff --git a/source/OpenAPI-Core/OAMediaTypeObject.class.st b/source/OpenAPI-Core/OAMediaTypeObject.class.st index 3e0cff4..290842c 100644 --- a/source/OpenAPI-Core/OAMediaTypeObject.class.st +++ b/source/OpenAPI-Core/OAMediaTypeObject.class.st @@ -39,6 +39,11 @@ OAMediaTypeObject >> encoding [ ^ encoding ] +{ #category : 'accessing' } +OAMediaTypeObject >> encoding: aDictionary [ + encoding := aDictionary +] + { #category : 'as yet unclassified' } OAMediaTypeObject >> example [ ^ example @@ -108,3 +113,10 @@ OAMediaTypeObject >> writeBody: aDictionary builder: builder [ ifFalse: [ schema readObject: aDictionary ]) ] + +{ #category : 'as yet unclassified' } +OAMediaTypeObject >> writeFormBody: aDictionary builder: builder [ + builder addFormBody: (schema isAnyObject + ifTrue: [ aDictionary ] + ifFalse: [ schema readObject: aDictionary ]) +] diff --git a/source/OpenAPI-Core/OAOperation.class.st b/source/OpenAPI-Core/OAOperation.class.st index 8a1c0a8..e47a261 100644 --- a/source/OpenAPI-Core/OAOperation.class.st +++ b/source/OpenAPI-Core/OAOperation.class.st @@ -52,11 +52,15 @@ OAOperation >> api [ ] { #category : 'as yet unclassified' } -OAOperation >> applyBody: body builder: builder [ - self hasBody ifTrue: [ | mediaTypeObject | - body ifNil: [ Error signal: 'cannot use nil for body' ]. - mediaTypeObject := self mediaTypeObjectFor: 'application/json'. - mediaTypeObject writeBody: body builder: builder ] +OAOperation >> applyBody: body builder: builder [ + | contentType mediaTypeObject | + self hasBody ifFalse: [ ^ self ]. + body ifNil: [ Error signal: 'cannot use nil for body' ]. + contentType := requestBody preferredContentType. + mediaTypeObject := self mediaTypeObjectFor: contentType. + contentType = 'application/x-www-form-urlencoded' + ifTrue: [ mediaTypeObject writeFormBody: body builder: builder ] + ifFalse: [ mediaTypeObject writeBody: body builder: builder ] ] { #category : 'as yet unclassified' } diff --git a/source/OpenAPI-Core/OARequestBody.class.st b/source/OpenAPI-Core/OARequestBody.class.st index 2c72914..4fa46e3 100644 --- a/source/OpenAPI-Core/OARequestBody.class.st +++ b/source/OpenAPI-Core/OARequestBody.class.st @@ -76,6 +76,14 @@ OARequestBody >> initialize [ required := false ] +{ #category : 'accessing' } +OARequestBody >> preferredContentType [ + content ifNil: [ ^ nil ]. + (content includesKey: 'application/x-www-form-urlencoded') ifTrue: [ ^ 'application/x-www-form-urlencoded' ]. + (content includesKey: 'application/json') ifTrue: [ ^ 'application/json' ]. + ^ content keys anyOne +] + { #category : 'instance creation' } OARequestBody >> readFrom: aCall object: object [ | mimeType bodyString | diff --git a/source/OpenAPI-Core/OASchemaDefinition.class.st b/source/OpenAPI-Core/OASchemaDefinition.class.st index 1f9eb82..8a8ff8f 100644 --- a/source/OpenAPI-Core/OASchemaDefinition.class.st +++ b/source/OpenAPI-Core/OASchemaDefinition.class.st @@ -2,7 +2,8 @@ Class { #name : 'OASchemaDefinition', #superclass : 'JSONSchemaDefinition', #instVars : [ - 'default' + 'default', + 'nullable' ], #category : 'OpenAPI-Core-Model', #package : 'OpenAPI-Core', @@ -13,7 +14,7 @@ Class { OASchemaDefinition class >> neoJsonMapping: mapper [ super neoJsonMapping: mapper. mapper for: self do: [ :mapping | - mapping mapInstVars: #( default ) ] + mapping mapInstVars: #( default nullable ) ] ] { #category : 'visiting' } @@ -30,3 +31,13 @@ OASchemaDefinition >> default [ OASchemaDefinition >> default: anObject [ default := anObject ] + +{ #category : 'accessing' } +OASchemaDefinition >> nullable [ + ^ nullable +] + +{ #category : 'accessing' } +OASchemaDefinition >> nullable: aBoolean [ + nullable := aBoolean +]