From c8a46a4eaf0eb9a826c8140dfd25c017e169097c Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Tue, 15 Jul 2025 10:37:49 -0300 Subject: [PATCH 1/5] Apply upstream changes --- .project | 3 +- .properties | 3 - .smalltalkci/.gitignore | 1 + LICENSE | 2 +- README.md | 5 +- ...ISkeletonCollectionExtensionsTest.class.st | 11 +- .../APISkeletonZnUrlExtensionsTest.class.st | 9 +- .../NeoJSONReaderExtensionsTest.class.st | 29 +-- .../NeoJSONWriterExtensionsTest.class.st | 9 +- .../RemoteReferenceTest.class.st | 11 +- source/API-Skeleton-Tests/package.st | 2 +- source/API-Skeleton/Collection.extension.st | 4 +- .../API-Skeleton/HTTPClientError.extension.st | 6 +- .../API-Skeleton/NeoJSONWriter.extension.st | 4 +- source/API-Skeleton/RemoteReference.class.st | 17 +- source/API-Skeleton/ZnUrl.extension.st | 4 +- source/API-Skeleton/package.st | 2 +- .../BaselineOfPersistentAPISkeleton.class.st | 73 +++---- .../package.st | 2 +- .../CreateEmptyRDBMSApplication.extension.st | 28 +-- ...anParameterDefinitionProvider.extension.st | 31 +++ ...eSQLDatabaseProviderModuleFactory.class.st | 9 +- .../package.st | 2 +- .../CreateEmptyRDBMSApplication.extension.st | 10 +- ...anParameterDefinitionProvider.extension.st | 15 ++ ...LiteDatabaseProviderModuleFactory.class.st | 15 +- .../Persistent-API-Skeleton-SQLite/package.st | 2 +- .../ConfigureAllowedOriginsTest.class.st | 52 +++++ .../CreateEmptyRDBMSApplicationTest.class.st | 19 +- .../PersistentAPIConfigurationTest.class.st | 11 +- .../PersistentPetStoreApplication.class.st | 67 +++++++ ...PersistentPetStoreApplicationTest.class.st | 178 ++++++++++++++++++ .../PetStoreInstallation.class.st | 37 ++++ ...QLCreateEmptyRDBMSApplicationTest.class.st | 71 ++++++- .../RDBMSMappingConfigurationTest.class.st | 11 +- ...teCreateEmptyRDBMSApplicationTest.class.st | 82 ++++++-- ...DatabaseProviderModuleFactoryTest.class.st | 9 +- ...DatabaseProviderModuleFactoryTest.class.st | 11 +- .../Persistent-API-Skeleton-Tests/package.st | 2 +- .../CompositeStackTraceDumper.class.st | 27 +++ .../ConfigureAllowedOrigins.class.st | 39 ++++ .../CreateEmptyRDBMSApplication.class.st | 59 +++--- .../PersistentAPIApplication.class.st | 158 ++++++++++------ .../RDBMSMappingConfiguration.extension.st | 8 +- .../SaganParameterDefinitionProvider.class.st | 92 +++++++++ ...eDatabaseRepositoryProviderModule.class.st | 19 +- ...seRepositoryProviderModuleFactory.class.st | 52 ++--- source/Persistent-API-Skeleton/package.st | 2 +- 48 files changed, 996 insertions(+), 319 deletions(-) delete mode 100644 .properties create mode 100644 .smalltalkci/.gitignore create mode 100644 source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st create mode 100644 source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st create mode 100644 source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st create mode 100644 source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st create mode 100644 source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st create mode 100644 source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st create mode 100644 source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st create mode 100644 source/Persistent-API-Skeleton/ConfigureAllowedOrigins.class.st create mode 100644 source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st diff --git a/.project b/.project index d7c7acd..9076c38 100644 --- a/.project +++ b/.project @@ -1,3 +1,4 @@ { - 'srcDirectory' : 'source' + 'srcDirectory' : 'source', + 'tags': [ 'Mercap' ] } diff --git a/.properties b/.properties deleted file mode 100644 index 53a5454..0000000 --- a/.properties +++ /dev/null @@ -1,3 +0,0 @@ -{ - #format : #tonel -} diff --git a/.smalltalkci/.gitignore b/.smalltalkci/.gitignore new file mode 100644 index 0000000..612687a --- /dev/null +++ b/.smalltalkci/.gitignore @@ -0,0 +1 @@ +coverage/* diff --git a/LICENSE b/LICENSE index 4ff589b..71ed897 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Mercap Software +Copyright (c) 2022-2025 Mercap Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6f6f9e0..458a7ab 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ includes as dependencies: Stargate, Sagan, Kepler and Launchpad [![GitHub release](https://img.shields.io/github/release/mercap/Persistent-API-Skeleton.svg)](https://github.com/mercap/Persistent-API-Skeleton/releases/latest) [![Pharo 10](https://img.shields.io/badge/Pharo-10-informational)](https://pharo.org) +[![Pharo 11](https://img.shields.io/badge/Pharo-11-informational)](https://pharo.org) +[![Pharo 12](https://img.shields.io/badge/Pharo-12-informational)](https://pharo.org) +[![Pharo 13](https://img.shields.io/badge/Pharo-13-informational)](https://pharo.org) ## Quick links @@ -24,7 +27,7 @@ includes as dependencies: Stargate, Sagan, Kepler and Launchpad ## Installation -To load the project in a Pharo image follow this [instructions](docs/how-to/how-to-load-in-pharo.md). +To load the project in a Pharo image follow these [instructions](docs/how-to/how-to-load-in-pharo.md). ## Contributing diff --git a/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st index cc1bd82..029e957 100644 --- a/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st @@ -1,16 +1,17 @@ Class { - #name : #APISkeletonCollectionExtensionsTest, - #superclass : #TestCase, - #category : #'API-Skeleton-Tests' + #name : 'APISkeletonCollectionExtensionsTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } APISkeletonCollectionExtensionsTest >> testAssociationArrayAsNeoJSON [ self assert: { 'a' -> 'b' } asNeoJSON a equals: 'b' ] -{ #category : #tests } +{ #category : 'tests' } APISkeletonCollectionExtensionsTest >> testDictionaryAsNeoJSON [ self assert: { #key -> 'value' } asDictionary asNeoJSON key equals: 'value' diff --git a/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st index 88600f0..fed2bcf 100644 --- a/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st @@ -1,10 +1,11 @@ Class { - #name : #APISkeletonZnUrlExtensionsTest, - #superclass : #TestCase, - #category : #'API-Skeleton-Tests' + #name : 'APISkeletonZnUrlExtensionsTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } APISkeletonZnUrlExtensionsTest >> testNotEmpty [ self diff --git a/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st b/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st index 2105cca..875ded5 100644 --- a/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st @@ -1,10 +1,11 @@ Class { - #name : #NeoJSONReaderExtensionsTest, - #superclass : #TestCase, - #category : #'API-Skeleton-Tests' + #name : 'NeoJSONReaderExtensionsTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #private } +{ #category : 'private' } NeoJSONReaderExtensionsTest >> readAssociationFrom: string [ | reader | @@ -21,7 +22,7 @@ NeoJSONReaderExtensionsTest >> readAssociationFrom: string [ ^ reader nextAs: Association ] -{ #category : #private } +{ #category : 'private' } NeoJSONReaderExtensionsTest >> readRemoteReferenceFrom: aJSONString [ | reader | @@ -41,7 +42,7 @@ NeoJSONReaderExtensionsTest >> readRemoteReferenceFrom: aJSONString [ ^ reader nextAs: Association ] -{ #category : #'tests - remote reference' } +{ #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromEmptyURL [ self @@ -50,7 +51,7 @@ NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromEmptyURL [ withMessageText: 'An empty location is not a valid URI' ] -{ #category : #'tests - remote reference' } +{ #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromInvalidURL [ self @@ -62,7 +63,7 @@ NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromInvalidURL [ withMessageText: 'Invalid URI' ] -{ #category : #'tests - remote reference' } +{ #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromNonStringObject [ self @@ -71,7 +72,7 @@ NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromNonStringObject [ withMessageText: '" expected' ] -{ #category : #'tests - strict mode' } +{ #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfKeyAndValuePropertiesAreMissing [ self @@ -80,7 +81,7 @@ NeoJSONReaderExtensionsTest >> testFailIfKeyAndValuePropertiesAreMissing [ withMessageText: 'Missing required keys (#key, #value)' ] -{ #category : #'tests - strict mode' } +{ #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfKeyPropertyIsMissing [ self @@ -89,7 +90,7 @@ NeoJSONReaderExtensionsTest >> testFailIfKeyPropertyIsMissing [ withMessageText: 'Missing required keys (#key)' ] -{ #category : #'tests - strict mode' } +{ #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfValuePropertyIsMissing [ self @@ -98,7 +99,7 @@ NeoJSONReaderExtensionsTest >> testFailIfValuePropertyIsMissing [ withMessageText: 'Missing required keys (#value)' ] -{ #category : #'tests - remote reference' } +{ #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testParseRemoteReference [ | object | @@ -112,7 +113,7 @@ NeoJSONReaderExtensionsTest >> testParseRemoteReference [ equals: 'https://api.example.com/resource/00000000-0000-0000-0000-000000000000' ] -{ #category : #'tests - strict mode' } +{ #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testParsingValidObject [ | object | @@ -123,7 +124,7 @@ NeoJSONReaderExtensionsTest >> testParsingValidObject [ assert: object value equals: 1 ] -{ #category : #'tests - strict mode' } +{ #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testParsingValidObjectWithExtraFields [ | object | diff --git a/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st b/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st index 508a09b..e59ee60 100644 --- a/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st @@ -1,10 +1,11 @@ Class { - #name : #NeoJSONWriterExtensionsTest, - #superclass : #TestCase, - #category : #'API-Skeleton-Tests' + #name : 'NeoJSONWriterExtensionsTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } NeoJSONWriterExtensionsTest >> testWriteRemoteReference [ | object json | diff --git a/source/API-Skeleton-Tests/RemoteReferenceTest.class.st b/source/API-Skeleton-Tests/RemoteReferenceTest.class.st index d0e4fdf..637dc66 100644 --- a/source/API-Skeleton-Tests/RemoteReferenceTest.class.st +++ b/source/API-Skeleton-Tests/RemoteReferenceTest.class.st @@ -2,12 +2,13 @@ A RemoteReferenceTest is a test class for testing the behavior of RemoteReference " Class { - #name : #RemoteReferenceTest, - #superclass : #TestCase, - #category : #'API-Skeleton-Tests' + #name : 'RemoteReferenceTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } RemoteReferenceTest >> testAccessing [ self @@ -15,7 +16,7 @@ RemoteReferenceTest >> testAccessing [ equals: 'https://google.com/' ] -{ #category : #tests } +{ #category : 'tests' } RemoteReferenceTest >> testComparing [ self diff --git a/source/API-Skeleton-Tests/package.st b/source/API-Skeleton-Tests/package.st index 15c6f79..dafe7de 100644 --- a/source/API-Skeleton-Tests/package.st +++ b/source/API-Skeleton-Tests/package.st @@ -1 +1 @@ -Package { #name : #'API-Skeleton-Tests' } +Package { #name : 'API-Skeleton-Tests' } diff --git a/source/API-Skeleton/Collection.extension.st b/source/API-Skeleton/Collection.extension.st index 7c828f9..70b3ded 100644 --- a/source/API-Skeleton/Collection.extension.st +++ b/source/API-Skeleton/Collection.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #Collection } +Extension { #name : 'Collection' } -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } Collection >> asNeoJSON [ "This method is intended to work on dictionaries or collections of associations" diff --git a/source/API-Skeleton/HTTPClientError.extension.st b/source/API-Skeleton/HTTPClientError.extension.st index 4b4a5a2..81b6a93 100644 --- a/source/API-Skeleton/HTTPClientError.extension.st +++ b/source/API-Skeleton/HTTPClientError.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #HTTPClientError } +Extension { #name : 'HTTPClientError' } -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } HTTPClientError >> asJRPCJSON [ ^ super asJRPCJSON @@ -8,7 +8,7 @@ HTTPClientError >> asJRPCJSON [ yourself ] -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } HTTPClientError >> asJRPCResponseWithId: anInteger [ ^ self code = 422 diff --git a/source/API-Skeleton/NeoJSONWriter.extension.st b/source/API-Skeleton/NeoJSONWriter.extension.st index 6211018..19cadf2 100644 --- a/source/API-Skeleton/NeoJSONWriter.extension.st +++ b/source/API-Skeleton/NeoJSONWriter.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #NeoJSONWriter } +Extension { #name : 'NeoJSONWriter' } -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } NeoJSONWriter >> mapRemoteReference [ self diff --git a/source/API-Skeleton/RemoteReference.class.st b/source/API-Skeleton/RemoteReference.class.st index 4f0d79d..41ef1f3 100644 --- a/source/API-Skeleton/RemoteReference.class.st +++ b/source/API-Skeleton/RemoteReference.class.st @@ -1,19 +1,20 @@ Class { - #name : #RemoteReference, - #superclass : #Object, + #name : 'RemoteReference', + #superclass : 'Object', #instVars : [ 'selfLocation' ], - #category : #'API-Skeleton' + #category : 'API-Skeleton', + #package : 'API-Skeleton' } -{ #category : #accessing } +{ #category : 'accessing' } RemoteReference class >> locatedAt: aUrl [ ^ self new initializeLocatedAt: aUrl ] -{ #category : #comparing } +{ #category : 'comparing' } RemoteReference >> = anObject [ ^ self equalityChecker @@ -21,19 +22,19 @@ RemoteReference >> = anObject [ checkAgainst: anObject ] -{ #category : #comparing } +{ #category : 'comparing' } RemoteReference >> hash [ ^ self selfLocation hash ] -{ #category : #initialization } +{ #category : 'initialization' } RemoteReference >> initializeLocatedAt: aUrl [ selfLocation := aUrl asString ] -{ #category : #initialization } +{ #category : 'initialization' } RemoteReference >> selfLocation [ ^ selfLocation diff --git a/source/API-Skeleton/ZnUrl.extension.st b/source/API-Skeleton/ZnUrl.extension.st index 27f76fa..b7fad05 100644 --- a/source/API-Skeleton/ZnUrl.extension.st +++ b/source/API-Skeleton/ZnUrl.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #ZnUrl } +Extension { #name : 'ZnUrl' } -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } ZnUrl >> notEmpty [ ^ self isEmpty not diff --git a/source/API-Skeleton/package.st b/source/API-Skeleton/package.st index 6819e3a..07bc16d 100644 --- a/source/API-Skeleton/package.st +++ b/source/API-Skeleton/package.st @@ -1 +1 @@ -Package { #name : #'API-Skeleton' } +Package { #name : 'API-Skeleton' } diff --git a/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st b/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st index 5e2cff0..535a2df 100644 --- a/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st +++ b/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st @@ -1,10 +1,11 @@ Class { - #name : #BaselineOfPersistentAPISkeleton, - #superclass : #BaselineOf, - #category : #BaselineOfPersistentAPISkeleton + #name : 'BaselineOfPersistentAPISkeleton', + #superclass : 'BaselineOf', + #category : 'BaselineOfPersistentAPISkeleton', + #package : 'BaselineOfPersistentAPISkeleton' } -{ #category : #baselines } +{ #category : 'baselines' } BaselineOfPersistentAPISkeleton >> baseline: spec [ @@ -16,49 +17,53 @@ BaselineOfPersistentAPISkeleton >> baseline: spec [ spec group: 'Deployment' with: #('SQLite3 Persistence' 'PostgreSQL Persistence'); group: 'CI' with: 'Tests'; - group: 'Dependent-SUnit-Extensions' with: #('Stargate-SUnit' 'Kepler-SUnit'); + group: 'Dependent-SUnit-Extensions' with: #('Stargate-SUnit' 'Kepler-SUnit' 'Launchpad-SUnit'); group: 'Tools' with: 'Stargate-Tools'; group: 'Development' with: #('Tests' 'Tools') ] ] -{ #category : #accessing } +{ #category : 'accessing' } BaselineOfPersistentAPISkeleton >> projectClass [ ^ MetacelloCypressBaselineProject ] -{ #category : #baselines } +{ #category : 'baselines' } BaselineOfPersistentAPISkeleton >> setUpDependencies: spec [ - spec - baseline: 'Stargate' with: [ spec repository: 'github://ba-st/Stargate:v8' ]; - project: 'Stargate-Deployment' copyFrom: 'Stargate' with: [ spec loads: 'Deployment' ]; - project: 'Stargate-SUnit' copyFrom: 'Stargate' with: [ spec loads: 'Dependent-SUnit-Extensions' ]; - project: 'Stargate-Tools' copyFrom: 'Stargate' with: [ spec loads: 'Tools' ]. - - spec - baseline: 'StargateConsul' with: [ spec repository: 'github://ba-st/Stargate-Consul:v4' ]; - project: 'Stargate-Consul-Deployment' - copyFrom: 'StargateConsul' - with: [ spec loads: 'Deployment' ]. - - spec - baseline: 'Sagan' with: [ spec repository: 'github://ba-st/Sagan:v6' ]; - project: 'Sagan-SQLite' copyFrom: 'Sagan' with: [ spec loads: 'SQLite' ]; - project: 'Sagan-PostgreSQL' copyFrom: 'Sagan' with: [ spec loads: 'PostgreSQL' ]. - - spec - baseline: 'Launchpad' with: [ spec repository: 'github://ba-st/Launchpad:v4' ]; - project: 'Launchpad-Deployment' copyFrom: 'Launchpad' with: [ spec loads: 'Deployment' ]. - - spec - baseline: 'Kepler' with: [ spec repository: 'github://ba-st/Kepler:v5' ]; - project: 'Kepler-Core' copyFrom: 'Kepler' with: [ spec loads: 'Core' ]; - project: 'Kepler-SUnit' copyFrom: 'Kepler' with: [ spec loads: 'Dependent-SUnit-Extensions' ] + spec + baseline: 'Stargate' with: [ spec repository: 'github://ba-st/Stargate:v11' ]; + project: 'Stargate-Deployment' copyFrom: 'Stargate' with: [ spec loads: 'Deployment' ]; + project: 'Stargate-SUnit' + copyFrom: 'Stargate' + with: [ spec loads: 'Dependent-SUnit-Extensions' ]; + project: 'Stargate-Tools' copyFrom: 'Stargate' with: [ spec loads: 'Tools' ]; + project: 'Stargate-Examples' copyFrom: 'Stargate' with: [ spec loads: 'Examples' ]. + + spec + baseline: 'StargateConsul' with: [ spec repository: 'github://ba-st/Stargate-Consul:v7' ]; + project: 'Stargate-Consul-Deployment' + copyFrom: 'StargateConsul' + with: [ spec loads: 'Deployment' ]. + + spec + baseline: 'Sagan' with: [ spec repository: 'github://ba-st/Sagan:v10' ]; + project: 'Sagan-SQLite' copyFrom: 'Sagan' with: [ spec loads: 'SQLite' ]; + project: 'Sagan-PostgreSQL' copyFrom: 'Sagan' with: [ spec loads: 'PostgreSQL' ]. + + spec + baseline: 'Launchpad' with: [ spec repository: 'github://ba-st/Launchpad:v7' ]; + project: 'Launchpad-Deployment' copyFrom: 'Launchpad' with: [ spec loads: 'Deployment' ]; + project: 'Launchpad-SUnit' copyFrom: 'Launchpad' with: [ spec loads: 'Dependent-SUnit-Extensions' ]. + + spec + baseline: 'Kepler' with: [ spec repository: 'github://ba-st/Kepler:v7' ]; + project: 'Kepler-Core' copyFrom: 'Kepler' with: [ spec loads: 'Core' ]; + project: 'Kepler-SUnit' copyFrom: 'Kepler' with: [ spec loads: 'Dependent-SUnit-Extensions' ] ] -{ #category : #baselines } +{ #category : 'baselines' } BaselineOfPersistentAPISkeleton >> setUpPackages: spec [ spec @@ -88,7 +93,7 @@ BaselineOfPersistentAPISkeleton >> setUpPackages: spec [ spec package: 'Persistent-API-Skeleton-Tests' with: [ spec - requires: #('Persistent-API-Skeleton-SQLite' 'Persistent-API-Skeleton-PostgreSQL' 'Stargate-SUnit') + requires: #('Persistent-API-Skeleton-SQLite' 'Persistent-API-Skeleton-PostgreSQL' 'Stargate-SUnit' 'Stargate-Examples' 'Launchpad-SUnit') ]; group: 'Tests' with: 'Persistent-API-Skeleton-Tests' ] diff --git a/source/BaselineOfPersistentAPISkeleton/package.st b/source/BaselineOfPersistentAPISkeleton/package.st index 9e947cf..12f8895 100644 --- a/source/BaselineOfPersistentAPISkeleton/package.st +++ b/source/BaselineOfPersistentAPISkeleton/package.st @@ -1 +1 @@ -Package { #name : #BaselineOfPersistentAPISkeleton } +Package { #name : 'BaselineOfPersistentAPISkeleton' } diff --git a/source/Persistent-API-Skeleton-PostgreSQL/CreateEmptyRDBMSApplication.extension.st b/source/Persistent-API-Skeleton-PostgreSQL/CreateEmptyRDBMSApplication.extension.st index 6db64f6..c01f426 100644 --- a/source/Persistent-API-Skeleton-PostgreSQL/CreateEmptyRDBMSApplication.extension.st +++ b/source/Persistent-API-Skeleton-PostgreSQL/CreateEmptyRDBMSApplication.extension.st @@ -1,29 +1,7 @@ -Extension { #name : #CreateEmptyRDBMSApplication } +Extension { #name : 'CreateEmptyRDBMSApplication' } -{ #category : #'*Persistent-API-Skeleton-PostgreSQL' } +{ #category : '*Persistent-API-Skeleton-PostgreSQL' } CreateEmptyRDBMSApplication class >> saganConfigurationParametersForPostgreSQL [ - ^ OrderedCollection new - add: ( MandatoryConfigurationParameter - named: 'PG Hostname' - describedBy: 'Name of PostgreSQL''s host to connect' - inside: self sectionsForSaganConfiguration ); - add: ( OptionalConfigurationParameter - named: 'PG Port' - describedBy: 'Port number to connect to at the server host' - inside: self sectionsForSaganConfiguration - defaultingTo: 5432 ); - add: ( MandatoryConfigurationParameter - named: 'PG Username' - describedBy: 'PostgreSQL user name to connect as' - inside: self sectionsForSaganConfiguration ); - add: ( MandatoryConfigurationParameter - named: 'PG Password' - describedBy: 'Password to be used if the server demands password authentication' - inside: self sectionsForSaganConfiguration ) asSensitive; - add: ( MandatoryConfigurationParameter - named: 'PG Database Name' - describedBy: 'The database name' - inside: self sectionsForSaganConfiguration ); - asArray + ^ SaganParameterDefinitionProvider saganConfigurationParametersForPostgreSQL ] diff --git a/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st b/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st new file mode 100644 index 0000000..6cb61b1 --- /dev/null +++ b/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st @@ -0,0 +1,31 @@ +Extension { #name : 'SaganParameterDefinitionProvider' } + +{ #category : '*Persistent-API-Skeleton-PostgreSQL' } +SaganParameterDefinitionProvider class >> saganConfigurationParametersForPostgreSQL [ + + ^ OrderedCollection new + add: ( MandatoryConfigurationParameter + named: 'PG Hostname' + describedBy: 'Name of PostgreSQL''s host to connect' + inside: self sectionsForSaganConfiguration ); + add: ( OptionalConfigurationParameter + named: 'PG Port' + describedBy: 'Port number to connect to at the server host' + inside: self sectionsForSaganConfiguration + defaultingTo: 5432 ); + add: ( MandatoryConfigurationParameter + named: 'PG Username' + describedBy: 'PostgreSQL user name to connect as' + inside: self sectionsForSaganConfiguration ); + add: ( MandatoryConfigurationParameter + named: 'PG Password' + describedBy: 'Password to be used if the server demands password authentication' + inside: self sectionsForSaganConfiguration ) asSensitive; + add: ( MandatoryConfigurationParameter + named: 'PG Database Name' + describedBy: 'The database name' + inside: self sectionsForSaganConfiguration ); + addAll: self saganBasicConfigurationParameters; + addAll: self saganSessionPoolingConfigurationParameters; + asArray +] diff --git a/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st b/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st index 01b0756..3d288db 100644 --- a/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st @@ -1,10 +1,11 @@ Class { - #name : #SinglePostgreSQLDatabaseProviderModuleFactory, - #superclass : #SingleDatabaseRepositoryProviderModuleFactory, - #category : #'Persistent-API-Skeleton-PostgreSQL' + #name : 'SinglePostgreSQLDatabaseProviderModuleFactory', + #superclass : 'SingleDatabaseRepositoryProviderModuleFactory', + #category : 'Persistent-API-Skeleton-PostgreSQL', + #package : 'Persistent-API-Skeleton-PostgreSQL' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } SinglePostgreSQLDatabaseProviderModuleFactory >> databaseLogin [ | login | diff --git a/source/Persistent-API-Skeleton-PostgreSQL/package.st b/source/Persistent-API-Skeleton-PostgreSQL/package.st index da9df24..8e4a31d 100644 --- a/source/Persistent-API-Skeleton-PostgreSQL/package.st +++ b/source/Persistent-API-Skeleton-PostgreSQL/package.st @@ -1 +1 @@ -Package { #name : #'Persistent-API-Skeleton-PostgreSQL' } +Package { #name : 'Persistent-API-Skeleton-PostgreSQL' } diff --git a/source/Persistent-API-Skeleton-SQLite/CreateEmptyRDBMSApplication.extension.st b/source/Persistent-API-Skeleton-SQLite/CreateEmptyRDBMSApplication.extension.st index e3af29c..7cb6c49 100644 --- a/source/Persistent-API-Skeleton-SQLite/CreateEmptyRDBMSApplication.extension.st +++ b/source/Persistent-API-Skeleton-SQLite/CreateEmptyRDBMSApplication.extension.st @@ -1,11 +1,7 @@ -Extension { #name : #CreateEmptyRDBMSApplication } +Extension { #name : 'CreateEmptyRDBMSApplication' } -{ #category : #'*Persistent-API-Skeleton-SQLite' } +{ #category : '*Persistent-API-Skeleton-SQLite' } CreateEmptyRDBMSApplication class >> saganConfigurationParametersForSQLite [ - ^ Array with: ( OptionalConfigurationParameter - named: 'Database File Name' - describedBy: 'SQLite Database file name' - inside: self sectionsForSaganConfiguration - defaultingTo: 'default.db' ) + ^ SaganParameterDefinitionProvider saganConfigurationParametersForSQLite ] diff --git a/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st b/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st new file mode 100644 index 0000000..a032c43 --- /dev/null +++ b/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st @@ -0,0 +1,15 @@ +Extension { #name : 'SaganParameterDefinitionProvider' } + +{ #category : '*Persistent-API-Skeleton-SQLite' } +SaganParameterDefinitionProvider class >> saganConfigurationParametersForSQLite [ + + ^ OrderedCollection new + add: ( OptionalConfigurationParameter + named: 'Database File Name' + describedBy: 'SQLite Database file name' + inside: self sectionsForSaganConfiguration + defaultingTo: 'default.db' ); + addAll: self saganBasicConfigurationParameters; + addAll: self saganSessionPoolingConfigurationParameters; + yourself +] diff --git a/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st b/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st index ba2b106..40ba7bd 100644 --- a/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st @@ -1,22 +1,23 @@ Class { - #name : #SingleSQLiteDatabaseProviderModuleFactory, - #superclass : #SingleDatabaseRepositoryProviderModuleFactory, - #category : #'Persistent-API-Skeleton-SQLite' + #name : 'SingleSQLiteDatabaseProviderModuleFactory', + #superclass : 'SingleDatabaseRepositoryProviderModuleFactory', + #category : 'Persistent-API-Skeleton-SQLite', + #package : 'Persistent-API-Skeleton-SQLite' } -{ #category : #private } +{ #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseBaseName [ ^self databaseFileName asFileReference asAbsolute basename ] -{ #category : #private } +{ #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseFileName [ ^databaseConfiguration databaseFileName ] -{ #category : #private } +{ #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseLogin [ DatabaseAccessor classForThisPlatform DefaultDriver: SQLite3Driver. @@ -28,7 +29,7 @@ SingleSQLiteDatabaseProviderModuleFactory >> databaseLogin [ yourself ] -{ #category : #private } +{ #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databasePathName [ ^ self databaseFileName asFileReference asAbsolute parent pathString diff --git a/source/Persistent-API-Skeleton-SQLite/package.st b/source/Persistent-API-Skeleton-SQLite/package.st index 56c8db3..52fef5f 100644 --- a/source/Persistent-API-Skeleton-SQLite/package.st +++ b/source/Persistent-API-Skeleton-SQLite/package.st @@ -1 +1 @@ -Package { #name : #'Persistent-API-Skeleton-SQLite' } +Package { #name : 'Persistent-API-Skeleton-SQLite' } diff --git a/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st b/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st new file mode 100644 index 0000000..a14dcb4 --- /dev/null +++ b/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st @@ -0,0 +1,52 @@ +Class { + #name : 'ConfigureAllowedOriginsTest', + #superclass : 'TestCase', + #instVars : [ + 'allowedOrigins', + 'exposedHeaders' + ], + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' +} + +{ #category : 'Mocking' } +ConfigureAllowedOriginsTest >> allowCrossOriginSharingApplying: aOneArgBlock [ + + aOneArgBlock value: self +] + +{ #category : 'Mocking' } +ConfigureAllowedOriginsTest >> allowOnlyFrom: aCollection [ + + allowedOrigins := aCollection +] + +{ #category : 'Mocking' } +ConfigureAllowedOriginsTest >> expose: aCollection [ + + exposedHeaders := aCollection +] + +{ #category : 'tests' } +ConfigureAllowedOriginsTest >> testApplyOnNoOriginsAllowed [ + + ( ConfigureAllowedOrigins allowing: #( ) exposing: #( ) ) applyOn: self. + + self + assert: allowedOrigins isEmpty; + assert: exposedHeaders isEmpty +] + +{ #category : 'tests' } +ConfigureAllowedOriginsTest >> testApplyOnOneOriginAllowed [ + + ( ConfigureAllowedOrigins + allowing: ( Array with: 'http://allowed-origin-test/' ) + exposing: ( Array with: 'Etag' ) ) applyOn: self. + + self + assert: allowedOrigins size equals: 1; + assert: allowedOrigins first equals: 'http://allowed-origin-test/'; + assert: exposedHeaders size equals: 1; + assert: exposedHeaders first equals: 'Etag' +] diff --git a/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st index 2dc094d..5d34f1b 100644 --- a/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st @@ -1,31 +1,26 @@ Class { - #name : #CreateEmptyRDBMSApplicationTest, - #superclass : #TestCase, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'CreateEmptyRDBMSApplicationTest', + #superclass : 'TestCase', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #testing } +{ #category : 'testing' } CreateEmptyRDBMSApplicationTest class >> isAbstract [ ^ self = CreateEmptyRDBMSApplicationTest ] -{ #category : #tests } +{ #category : 'tests' } CreateEmptyRDBMSApplicationTest >> testCommandName [ self assert: CreateEmptyRDBMSApplication commandName equals: 'create-empty-rdbms' ] -{ #category : #tests } +{ #category : 'tests' } CreateEmptyRDBMSApplicationTest >> testDescription [ self assert: CreateEmptyRDBMSApplication description equals: 'Creates the database structure' ] - -{ #category : #tests } -CreateEmptyRDBMSApplicationTest >> testSectionsForSaganConfiguration [ - - self assert: CreateEmptyRDBMSApplication sectionsForSaganConfiguration equals: #( 'Sagan' ) -] diff --git a/source/Persistent-API-Skeleton-Tests/PersistentAPIConfigurationTest.class.st b/source/Persistent-API-Skeleton-Tests/PersistentAPIConfigurationTest.class.st index 9eda9cc..5d8ddc1 100644 --- a/source/Persistent-API-Skeleton-Tests/PersistentAPIConfigurationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/PersistentAPIConfigurationTest.class.st @@ -2,18 +2,19 @@ A PersistentAPIConfigurationTest is a test class for testing the behavior of PersistentAPIConfiguration " Class { - #name : #PersistentAPIConfigurationTest, - #superclass : #TestCase, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'PersistentAPIConfigurationTest', + #superclass : 'TestCase', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #testing } +{ #category : 'testing' } PersistentAPIConfigurationTest class >> isAbstract [ ^ self name = #PersistentAPIConfigurationTest ] -{ #category : #private } +{ #category : 'private' } PersistentAPIConfigurationTest >> commandLineProviderOver: arguments [ ^ ConfigurationFromCommandLineProvider over: ( CommandLineArguments withArguments: arguments ) diff --git a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st new file mode 100644 index 0000000..d08a8dc --- /dev/null +++ b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st @@ -0,0 +1,67 @@ +Class { + #name : 'PersistentPetStoreApplication', + #superclass : 'PersistentAPIApplication', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' +} + +{ #category : 'private' } +PersistentPetStoreApplication class >> commandName [ + + ^ 'persistent-pet-store' +] + +{ #category : 'initialization' } +PersistentPetStoreApplication class >> description [ + + ^ 'A RESTful API for Pet stores' +] + +{ #category : 'initialization' } +PersistentPetStoreApplication class >> initialize [ + + + self initializeVersion +] + +{ #category : 'private' } +PersistentPetStoreApplication class >> projectName [ + + ^ #Stargate +] + +{ #category : 'initialization' } +PersistentPetStoreApplication class >> saganConfigurationParameters [ + + ^#() +] + +{ #category : 'private' } +PersistentPetStoreApplication class >> sectionsForStargateConfiguration [ + + ^ #( 'Pet Store' ) , super sectionsForStargateConfiguration +] + +{ #category : 'private - accessing' } +PersistentPetStoreApplication >> controllersToInstall [ + + ^ { PetsRESTfulController new. PetOrdersRESTfulController new } +] + +{ #category : 'private - accessing' } +PersistentPetStoreApplication >> installation [ + + ^ PetStoreInstallation installedBy: self +] + +{ #category : 'private - accessing' } +PersistentPetStoreApplication >> serviceDefinitions [ + + ^ #() +] + +{ #category : 'private - accessing' } +PersistentPetStoreApplication >> stargateConfiguration [ + + ^self configuration petStore stargate +] diff --git a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st new file mode 100644 index 0000000..cc13d8c --- /dev/null +++ b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st @@ -0,0 +1,178 @@ +Class { + #name : 'PersistentPetStoreApplicationTest', + #superclass : 'LaunchpadTest', + #instVars : [ + 'port', + 'petRepository', + 'allowedOrigins', + 'exposedHeaders' + ], + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' +} + +{ #category : 'configuring' } +PersistentPetStoreApplicationTest >> allowCrossOriginSharingApplying: aFullBlockClosure [ + aFullBlockClosure value: self. +] + +{ #category : 'configuring' } +PersistentPetStoreApplicationTest >> allowOnlyFrom: aCollection [ + allowedOrigins := aCollection +] + +{ #category : 'configuring' } +PersistentPetStoreApplicationTest >> expose: aCollection [ + exposedHeaders := aCollection +] + +{ #category : 'running' } +PersistentPetStoreApplicationTest >> setUp [ + + super setUp. + port := self freeListeningTCPPort. + StargateApplication logsDirectory ensureCreateDirectory. + petRepository := InMemoryPetRepository new. + self assert: petRepository findAll isEmpty. + allowedOrigins := #(). + exposedHeaders := #(). +] + +{ #category : 'tests' } +PersistentPetStoreApplicationTest >> testAllowedOriginParameter [ + + | logRecords | + self start: PersistentPetStoreApplication withAll: { + ('--pet-store.stargate.public-url=http://localhost:<1p>' + expandMacrosWith: port). + ('--pet-store.stargate.port=<1p>' expandMacrosWith: port). + ('--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: + 'secret'). + '--pet-store.stargate.allowed-origins=http://localhost/,http://test:3000/'. + '--pet-store.stargate.consul-agent-location=' }. + + runningApplication configuration parameters + detect: [ :parameter | parameter name = 'Allowed Origins' ] + ifFound: [ :parameter | + self + assert: parameter summary + equals: + 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; + assert: parameter commandLineArgumentName + equals: 'pet-store.stargate.allowed-origins'; + assert: parameter environmentVariableName + equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. + self assert: allowedOrigins isEmpty. + (runningApplication configuration valueFor: parameter) value: self. + self assertCollection: allowedOrigins hasSameElements: { + (WebOrigin basedOn: 'http://localhost/' asUrl). + (WebOrigin basedOn: 'http://test:3000/' asUrl) } ] + ifNone: [ self fail: 'Allowed Origins parameter not found' ]. + logRecords := OrderedCollection new. + logRecords + add: '[INFO] persistent-pet-store'; + add: '[INFO] Obtaining configuration.'; + add: '[WARNING] "Log HTTP Requests" parameter not provided.'; + add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; + add: '[WARNING] "Scheme" parameter not provided. Using default'; + add: + '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; + add: + '[WARNING] "Service Discovery Timeout ms" parameter not provided'; + add: + '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; + add: '[INFO] Public URL:'; + add: '[INFO] Port:'; + add: '[INFO] Operations Secret'; + add: '[INFO] Log HTTP Requests: false'; + add: '[INFO] Concurrent Connections Threshold: 32'; + add: '[INFO] Consul Agent Location'; + add: '[INFO] Scheme: http'; + add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; + add: '[INFO] Service Discovery Timeout ms: 60000'; + add: '[INFO] Service Discovery Time Slot between retries in ms: 100'; + add: + 'Allowed Origins: [ :api | self configureAllowOrigins: webOrigins to: api ]'; + add: '[INFO] Obtaining configuration... [DONE]'; + add: '[INFO] Installing system'; + add: '[INFO] Starting up system'; + add: '[INFO] API Version:'; + add: '[INFO] Creating API...'; + add: '[INFO] Creating API... [DONE]'; + add: '[INFO] Enabling CORS...'; + add: '[INFO] Enabling CORS... [DONE]'; + add: '[INFO] Installing API...'; + add: '[INFO] Installing API... [DONE]'; + add: '[INFO] Starting API...'; + add: '[INFO] Starting API... [DONE]'. + self assertLogRecordsMatch: logRecords +] + +{ #category : 'tests' } +PersistentPetStoreApplicationTest >> testMissingAllowedOriginParameter [ + + | logRecords | + self start: PersistentPetStoreApplication withAll: { + ('--pet-store.stargate.public-url=http://localhost:<1p>' + expandMacrosWith: port). + ('--pet-store.stargate.port=<1p>' expandMacrosWith: port). + ('--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: + 'secret'). + '--pet-store.stargate.consul-agent-location=' }. + + runningApplication configuration parameters + detect: [ :parameter | parameter name = 'Allowed Origins' ] + ifFound: [ :parameter | + self + assert: parameter summary + equals: + 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; + assert: parameter commandLineArgumentName + equals: 'pet-store.stargate.allowed-origins'; + assert: parameter environmentVariableName + equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. + self assert: allowedOrigins isEmpty. + self assert: exposedHeaders isEmpty. + (runningApplication configuration valueFor: parameter) value: self. + self assert: allowedOrigins isEmpty. + self assert: exposedHeaders isEmpty. + logRecords := OrderedCollection new. + logRecords + add: '[INFO] persistent-pet-store'; + add: '[INFO] Obtaining configuration.'; + add: '[WARNING] "Log HTTP Requests" parameter not provided.'; + add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; + add: '[WARNING] "Scheme" parameter not provided. Using default'; + add: + '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; + add: + '[WARNING] "Service Discovery Timeout ms" parameter not provided'; + add: + '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; + add: + '[WARNING] "Allowed Origins" parameter not provided. Using default.'; + add: '[INFO] Public URL:'; + add: '[INFO] Port:'; + add: '[INFO] Operations Secret'; + add: '[INFO] Log HTTP Requests: false'; + add: '[INFO] Concurrent Connections Threshold: 32'; + add: '[INFO] Consul Agent Location'; + add: '[INFO] Scheme: http'; + add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; + add: '[INFO] Service Discovery Timeout ms: 60000'; + add: + '[INFO] Service Discovery Time Slot between retries in ms: 100'; + add: '[INFO] Allowed Origins: [ :api | ]'; + add: '[INFO] Obtaining configuration... [DONE]'; + add: '[INFO] Installing system'; + add: '[INFO] Starting up system'; + add: '[INFO] API Version: '; + add: '[INFO] Creating API...'; + add: '[INFO] Creating API... [DONE]'; + add: '[INFO] Installing API...'; + add: '[INFO] Installing API... [DONE]'; + add: '[INFO] Starting API...'; + add: '[INFO] Starting API... [DONE]'. + self assertLogRecordsMatch: logRecords ] + ifNone: [ self fail: 'Allowed Origins parameter not found' ] +] diff --git a/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st b/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st new file mode 100644 index 0000000..3006f8f --- /dev/null +++ b/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st @@ -0,0 +1,37 @@ +Class { + #name : 'PetStoreInstallation', + #superclass : 'SystemInstallation', + #instVars : [ + 'application' + ], + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' +} + +{ #category : 'instance creation' } +PetStoreInstallation class >> installedBy: anApplication [ + +^self new initializeInstalledBy: anApplication +] + +{ #category : 'installing' } +PetStoreInstallation >> beAwareOfShutDownOf: aCompositeSystem [ +] + +{ #category : 'installing' } +PetStoreInstallation >> initializeInstalledBy: anApplication [ + +application := anApplication +] + +{ #category : 'installing' } +PetStoreInstallation >> modulesToInstall [ + + ^#() +] + +{ #category : 'installing' } +PetStoreInstallation >> name [ + + ^'Pet Store' +] diff --git a/source/Persistent-API-Skeleton-Tests/PostgreSQLCreateEmptyRDBMSApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/PostgreSQLCreateEmptyRDBMSApplicationTest.class.st index f0f95c6..a8bf179 100644 --- a/source/Persistent-API-Skeleton-Tests/PostgreSQLCreateEmptyRDBMSApplicationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/PostgreSQLCreateEmptyRDBMSApplicationTest.class.st @@ -1,17 +1,18 @@ Class { - #name : #PostgreSQLCreateEmptyRDBMSApplicationTest, - #superclass : #CreateEmptyRDBMSApplicationTest, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'PostgreSQLCreateEmptyRDBMSApplicationTest', + #superclass : 'CreateEmptyRDBMSApplicationTest', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } PostgreSQLCreateEmptyRDBMSApplicationTest >> testConfigurationParameters [ | parameters parameter | parameters := CreateEmptyRDBMSApplication saganConfigurationParametersForPostgreSQL. - self assert: parameters size equals: 5. + self assert: parameters size equals: 11. parameter := parameters first. @@ -47,11 +48,67 @@ PostgreSQLCreateEmptyRDBMSApplicationTest >> testConfigurationParameters [ assert: parameter commandLineArgumentName equals: 'sagan.pg-password'; assert: parameter environmentVariableName equals: 'SAGAN__PG_PASSWORD'. - parameter := parameters last. + parameter := parameters fifth. self assert: parameter name equals: 'PG Database Name'; assert: parameter summary equals: 'The database name'; assert: parameter commandLineArgumentName equals: 'sagan.pg-database-name'; - assert: parameter environmentVariableName equals: 'SAGAN__PG_DATABASE_NAME' + assert: parameter environmentVariableName equals: 'SAGAN__PG_DATABASE_NAME'. + + parameter := parameters sixth. + + self + assert: parameter name equals: 'Log Database Events'; + assert: parameter summary + equals: 'A boolean indicating whether should log database events or not. Defaults to false'; + assert: parameter commandLineArgumentName equals: 'sagan.log-database-events'; + assert: parameter environmentVariableName equals: 'SAGAN__LOG_DATABASE_EVENTS'. + + parameter := parameters seventh. + + self + assert: parameter name equals: 'Maximum Connection Attemps'; + assert: parameter summary equals: 'Number of retries login database attempts. Defaults to 3'; + assert: parameter commandLineArgumentName equals: 'sagan.maximum-connection-attemps'; + assert: parameter environmentVariableName equals: 'SAGAN__MAXIMUM_CONNECTION_ATTEMPS'. + + parameter := parameters eighth. + + self + assert: parameter name equals: 'Time Slot Between Connection Retries In ms'; + assert: parameter summary + equals: + 'Timeframe in milliseconds to wait before retrying a login to database. Defaults to 0:00:00:03'; + assert: parameter commandLineArgumentName + equals: 'sagan.time-slot-between-connection-retries-in-ms'; + assert: parameter environmentVariableName + equals: 'SAGAN__TIME_SLOT_BETWEEN_CONNECTION_RETRIES_IN_MS'. + + parameter := parameters ninth. + + self + assert: parameter name equals: 'Min Idle Sessions Count'; + assert: parameter summary + equals: 'Minimum number of idle sessions to keep alive in the pool. Defaults to 2'; + assert: parameter commandLineArgumentName equals: 'sagan.min-idle-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MIN_IDLE_SESSIONS_COUNT'. + + parameter := parameters at: 10. + + self + assert: parameter name equals: 'Max Idle Sessions Count'; + assert: parameter summary + equals: 'Maximum number of idle sessions to keep alive in the pool. Defaults to 5'; + assert: parameter commandLineArgumentName equals: 'sagan.max-idle-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MAX_IDLE_SESSIONS_COUNT'. + + parameter := parameters last. + + self + assert: parameter name equals: 'Max Active Sessions Count'; + assert: parameter summary equals: 'Maximum number of sessions alive. Defaults to 5'; + assert: parameter commandLineArgumentName equals: 'sagan.max-active-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MAX_ACTIVE_SESSIONS_COUNT'. + ] diff --git a/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st b/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st index 4cf25c1..7bfaa88 100644 --- a/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st @@ -2,12 +2,13 @@ A RDBMSMappingConfigurationTest is a test class for testing the behavior of RDBMSMappingConfiguration " Class { - #name : #RDBMSMappingConfigurationTest, - #superclass : #TestCase, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'RDBMSMappingConfigurationTest', + #superclass : 'TestCase', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } RDBMSMappingConfigurationTest >> testRemoteReferenceMapping [ | definition | @@ -34,7 +35,7 @@ RDBMSMappingConfigurationTest >> testRemoteReferenceMapping [ ] ] -{ #category : #tests } +{ #category : 'tests' } RDBMSMappingConfigurationTest >> testUUIDMapping [ | definition | diff --git a/source/Persistent-API-Skeleton-Tests/SQLiteCreateEmptyRDBMSApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/SQLiteCreateEmptyRDBMSApplicationTest.class.st index b1a3b63..d6603ae 100644 --- a/source/Persistent-API-Skeleton-Tests/SQLiteCreateEmptyRDBMSApplicationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/SQLiteCreateEmptyRDBMSApplicationTest.class.st @@ -1,21 +1,79 @@ Class { - #name : #SQLiteCreateEmptyRDBMSApplicationTest, - #superclass : #CreateEmptyRDBMSApplicationTest, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'SQLiteCreateEmptyRDBMSApplicationTest', + #superclass : 'CreateEmptyRDBMSApplicationTest', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } SQLiteCreateEmptyRDBMSApplicationTest >> testConfigurationParameters [ - | parameters | + | parameters parameter | parameters := CreateEmptyRDBMSApplication saganConfigurationParametersForSQLite. - self withTheOnlyOneIn: parameters do: [ :parameter | - self - assert: parameter name equals: 'Database File Name'; - assert: parameter summary equals: 'SQLite Database file name. Defaults to default.db'; - assert: parameter commandLineArgumentName equals: 'sagan.database-file-name'; - assert: parameter environmentVariableName equals: 'SAGAN__DATABASE_FILE_NAME' - ] + self assert: parameters size equals: 7. + + parameter := parameters first. + self + assert: parameter name equals: 'Database File Name'; + assert: parameter summary equals: 'SQLite Database file name. Defaults to default.db'; + assert: parameter commandLineArgumentName equals: 'sagan.database-file-name'; + assert: parameter environmentVariableName equals: 'SAGAN__DATABASE_FILE_NAME'. + + parameter := parameters second. + + self + assert: parameter name equals: 'Log Database Events'; + assert: parameter summary + equals: 'A boolean indicating whether should log database events or not. Defaults to false'; + assert: parameter commandLineArgumentName equals: 'sagan.log-database-events'; + assert: parameter environmentVariableName equals: 'SAGAN__LOG_DATABASE_EVENTS'. + + parameter := parameters third. + + self + assert: parameter name equals: 'Maximum Connection Attemps'; + assert: parameter summary equals: 'Number of retries login database attempts. Defaults to 3'; + assert: parameter commandLineArgumentName equals: 'sagan.maximum-connection-attemps'; + assert: parameter environmentVariableName equals: 'SAGAN__MAXIMUM_CONNECTION_ATTEMPS'. + + parameter := parameters fourth. + + self + assert: parameter name equals: 'Time Slot Between Connection Retries In ms'; + assert: parameter summary + equals: + 'Timeframe in milliseconds to wait before retrying a login to database. Defaults to 0:00:00:03'; + assert: parameter commandLineArgumentName + equals: 'sagan.time-slot-between-connection-retries-in-ms'; + assert: parameter environmentVariableName + equals: 'SAGAN__TIME_SLOT_BETWEEN_CONNECTION_RETRIES_IN_MS'. + + parameter := parameters fifth. + + self + assert: parameter name equals: 'Min Idle Sessions Count'; + assert: parameter summary + equals: 'Minimum number of idle sessions to keep alive in the pool. Defaults to 2'; + assert: parameter commandLineArgumentName equals: 'sagan.min-idle-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MIN_IDLE_SESSIONS_COUNT'. + + parameter := parameters sixth. + + self + assert: parameter name equals: 'Max Idle Sessions Count'; + assert: parameter summary + equals: 'Maximum number of idle sessions to keep alive in the pool. Defaults to 5'; + assert: parameter commandLineArgumentName equals: 'sagan.max-idle-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MAX_IDLE_SESSIONS_COUNT'. + + parameter := parameters last. + + self + assert: parameter name equals: 'Max Active Sessions Count'; + assert: parameter summary equals: 'Maximum number of sessions alive. Defaults to 5'; + assert: parameter commandLineArgumentName equals: 'sagan.max-active-sessions-count'; + assert: parameter environmentVariableName equals: 'SAGAN__MAX_ACTIVE_SESSIONS_COUNT'. + ] diff --git a/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st b/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st index 62683b0..a5bd987 100644 --- a/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st @@ -1,10 +1,11 @@ Class { - #name : #SinglePostgreSQLDatabaseProviderModuleFactoryTest, - #superclass : #PersistentAPIConfigurationTest, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'SinglePostgreSQLDatabaseProviderModuleFactoryTest', + #superclass : 'PersistentAPIConfigurationTest', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } SinglePostgreSQLDatabaseProviderModuleFactoryTest >> testDatabaseLogin [ | login configuration | diff --git a/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st b/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st index 1e4fa96..0138a73 100644 --- a/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st @@ -2,12 +2,13 @@ A SingleSQLiteDatabaseProviderModuleFactoryTest is a test class for testing the behavior of SingleSQLiteDatabaseProviderModuleFactory " Class { - #name : #SingleSQLiteDatabaseProviderModuleFactoryTest, - #superclass : #PersistentAPIConfigurationTest, - #category : #'Persistent-API-Skeleton-Tests' + #name : 'SingleSQLiteDatabaseProviderModuleFactoryTest', + #superclass : 'PersistentAPIConfigurationTest', + #category : 'Persistent-API-Skeleton-Tests', + #package : 'Persistent-API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } SingleSQLiteDatabaseProviderModuleFactoryTest >> testDatabaseLogin [ | factory configuration | @@ -26,7 +27,7 @@ SingleSQLiteDatabaseProviderModuleFactoryTest >> testDatabaseLogin [ assert: factory databaseLogin password isEmpty ] -{ #category : #tests } +{ #category : 'tests' } SingleSQLiteDatabaseProviderModuleFactoryTest >> testDefaultDatabaseLogin [ | factory configuration | diff --git a/source/Persistent-API-Skeleton-Tests/package.st b/source/Persistent-API-Skeleton-Tests/package.st index 5b9a4bc..d65af1f 100644 --- a/source/Persistent-API-Skeleton-Tests/package.st +++ b/source/Persistent-API-Skeleton-Tests/package.st @@ -1 +1 @@ -Package { #name : #'Persistent-API-Skeleton-Tests' } +Package { #name : 'Persistent-API-Skeleton-Tests' } diff --git a/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st b/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st new file mode 100644 index 0000000..1c7c174 --- /dev/null +++ b/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st @@ -0,0 +1,27 @@ +Class { + #name : 'CompositeStackTraceDumper', + #superclass : 'StackTraceDumper', + #instVars : [ + 'dumpers' + ], + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' +} + +{ #category : 'instance creation' } +CompositeStackTraceDumper class >> composingAll: dumpers [ + + ^ self new initializeComposingAll: dumpers +] + +{ #category : 'initialization' } +CompositeStackTraceDumper >> dumpStackTraceFor: anError [ + + dumpers do: [ :dumper | dumper dumpStackTraceFor: anError ] +] + +{ #category : 'initialization' } +CompositeStackTraceDumper >> initializeComposingAll: aDumpersCollection [ + + dumpers := aDumpersCollection +] diff --git a/source/Persistent-API-Skeleton/ConfigureAllowedOrigins.class.st b/source/Persistent-API-Skeleton/ConfigureAllowedOrigins.class.st new file mode 100644 index 0000000..17c6071 --- /dev/null +++ b/source/Persistent-API-Skeleton/ConfigureAllowedOrigins.class.st @@ -0,0 +1,39 @@ +Class { + #name : 'ConfigureAllowedOrigins', + #superclass : 'Object', + #instVars : [ + 'allowedUrls', + 'exposedHeaders' + ], + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' +} + +{ #category : 'instance creation' } +ConfigureAllowedOrigins class >> allowing: anUrlCollection exposing: aHeaderCollection [ + + ^ self new initializeAllowing: anUrlCollection exposing: aHeaderCollection +] + +{ #category : 'applying' } +ConfigureAllowedOrigins >> applyOn: anAPI [ + + anAPI allowCrossOriginSharingApplying: [ :cors | + cors + allowOnlyFrom: allowedUrls; + expose: exposedHeaders + ] +] + +{ #category : 'initialization' } +ConfigureAllowedOrigins >> initializeAllowing: anUrlCollection [ + + allowedUrls := anUrlCollection +] + +{ #category : 'initialization' } +ConfigureAllowedOrigins >> initializeAllowing: anUrlCollection exposing: aHeaderCollection [ + + allowedUrls := anUrlCollection. + exposedHeaders := aHeaderCollection +] diff --git a/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st b/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st index 07e8227..fb5e351 100644 --- a/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st +++ b/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st @@ -1,109 +1,104 @@ Class { - #name : #CreateEmptyRDBMSApplication, - #superclass : #LaunchpadApplication, + #name : 'CreateEmptyRDBMSApplication', + #superclass : 'LaunchpadApplication', #classInstVars : [ 'Version' ], - #category : #'Persistent-API-Skeleton' + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' } -{ #category : #accessing } -CreateEmptyRDBMSApplication class >> applicationBaselineName [ - - self subclassResponsibility -] - -{ #category : #accessing } +{ #category : 'accessing' } CreateEmptyRDBMSApplication class >> commandName [ ^ 'create-empty-rdbms' ] -{ #category : #accessing } +{ #category : 'accessing' } CreateEmptyRDBMSApplication class >> configurationParameters [ ^ self saganConfigurationParameters ] -{ #category : #accessing } +{ #category : 'accessing' } CreateEmptyRDBMSApplication class >> description [ ^ 'Creates the database structure' ] -{ #category : #private } +{ #category : 'private' } CreateEmptyRDBMSApplication class >> fileReferenceToDumpStackTrace [ ^ self logsDirectory / ( '<1s>-<2s>.fuel' expandMacrosWith: self commandName with: ( ( ZTimestampFormat fromString: '2001-02-03_16-05-06.07' ) format: ZTimestamp now ) ) ] -{ #category : #initialization } +{ #category : 'initialization' } CreateEmptyRDBMSApplication class >> initializeVersion [ - Version := VersionFromRepositoryResolver new valueFor: self applicationBaselineName + Version := VersionFromRepositoryResolver new valueFor: self projectName ] -{ #category : #testing } +{ #category : 'testing' } CreateEmptyRDBMSApplication class >> isAbstract [ ^ self = CreateEmptyRDBMSApplication ] -{ #category : #private } +{ #category : 'private' } CreateEmptyRDBMSApplication class >> logsDirectory [ - ^ FileLocator workingDirectory / 'logs' + ^ ( FileLocator workingDirectory / 'logs' ) ensureCreateDirectory ] -{ #category : #accessing } -CreateEmptyRDBMSApplication class >> saganConfigurationParameters [ +{ #category : 'accessing' } +CreateEmptyRDBMSApplication class >> projectName [ self subclassResponsibility ] -{ #category : #accessing } -CreateEmptyRDBMSApplication class >> sectionsForSaganConfiguration [ +{ #category : 'accessing' } +CreateEmptyRDBMSApplication class >> saganConfigurationParameters [ - ^ #( 'Sagan' ) + self subclassResponsibility ] -{ #category : #versions } +{ #category : 'versions' } CreateEmptyRDBMSApplication class >> version [ ^ Version ] -{ #category : #'private - activation' } +{ #category : 'private - activation' } CreateEmptyRDBMSApplication >> basicStartWithin: context [ | rootSystem | - LaunchpadLogRecord emitInfo: 'Installing system'. + LogRecord emitInfo: 'Installing system'. rootSystem := self installation install: self class version. - LaunchpadLogRecord emitInfo: 'Starting up system'. + LogRecord emitInfo: 'Starting up system'. rootSystem startUp. - LaunchpadLogRecord emitInfo: 'Setting up database structure'. + LogRecord emitInfo: 'Setting up database structure'. ( rootSystem >> #RepositoryProviderSystem ) prepareForInitialPersistence. - LaunchpadLogRecord emitInfo: 'Shutting down system'. + LogRecord emitInfo: 'Shutting down system'. rootSystem shutDown. self exitSuccess ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CreateEmptyRDBMSApplication >> installation [ ^ self subclassResponsibility ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } CreateEmptyRDBMSApplication >> saganConfiguration [ ^ self configuration sagan ] -{ #category : #'error handling' } +{ #category : 'error handling' } CreateEmptyRDBMSApplication >> stackTraceDumper [ ^ StackTraceBinarySerializer on: [ :dumpAction | diff --git a/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st b/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st index bd91383..4fdbb80 100644 --- a/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st +++ b/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st @@ -1,79 +1,85 @@ Class { - #name : #PersistentAPIApplication, - #superclass : #ConsulAwareStargateApplication, + #name : 'PersistentAPIApplication', + #superclass : 'ConsulAwareStargateApplication', #instVars : [ 'rootSystem' ], - #category : #'Persistent-API-Skeleton' + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' } -{ #category : #accessing } -PersistentAPIApplication class >> configurationParameters [ +{ #category : 'private' } +PersistentAPIApplication class >> allowedOriginsConfigurationParameter [ - ^ super configurationParameters , self saganConfigurationParameters + ^ OptionalConfigurationParameter + named: 'Allowed Origins' + describedBy: + 'A comma-separated list of URLs used as allowed origins for CORS' + inside: self sectionsForStargateConfiguration + defaultingTo: [ :api | ] + convertingWith: [ :string | + | webOrigins | + webOrigins := self parseWebOriginsFrom: string. + [ :api | self configureAllowOrigins: webOrigins to: api ] ] ] -{ #category : #private } -PersistentAPIApplication class >> defaultMaxActiveSessionsCount [ +{ #category : 'accessing' } +PersistentAPIApplication class >> configurationParameters [ - ^ self subclassResponsibility + ^ super configurationParameters , self saganConfigurationParameters ] -{ #category : #private } -PersistentAPIApplication class >> defaultMaxIdleSessionsCount [ +{ #category : 'private' } +PersistentAPIApplication class >> configureAllowOrigins: allowedOrigins to: api [ - ^ self subclassResponsibility + ^ LogRecord + emitInfo: 'Enabling CORS' + during: [ + (ConfigureAllowedOrigins allowing: allowedOrigins exposing: self exposedHeaders) applyOn: api ] ] -{ #category : #private } -PersistentAPIApplication class >> defaultMinIdleSessionsCount [ +{ #category : 'private - accessing' } +PersistentAPIApplication class >> exposedHeaders [ - ^ self subclassResponsibility + ^ OrderedCollection with: 'Etag' ] -{ #category : #testing } +{ #category : 'testing' } PersistentAPIApplication class >> isAbstract [ ^ self = PersistentAPIApplication ] -{ #category : #accessing } +{ #category : 'private' } +PersistentAPIApplication class >> parseWebOriginsFrom: string [ + + ^ [ (string splitOn: $,) collect: #asWebOrigin as: Array ] + on: InstanceCreationFailed + do: [ :error | + | parameter | + parameter := self allowedOriginsConfigurationParameter. + LogRecord emitError: + ('"<1s>" parameter value is invalid. <2s>' + expandMacrosWith: parameter name + with: error messageText). + RequiredConfigurationNotFound signal: + ('"<1s>" parameter value is invalid.' expandMacrosWith: + parameter name) ] +] + +{ #category : 'accessing' } PersistentAPIApplication class >> saganConfigurationParameters [ ^ self subclassResponsibility ] -{ #category : #private } -PersistentAPIApplication class >> saganSessionPoolingConfigurationParameters [ - - ^ Array - with: ( OptionalConfigurationParameter - named: 'Min Idle Sessions Count' - describedBy: 'Minimum number of idle sessions to keep alive in the pool' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMinIdleSessionsCount - convertingWith: #asNumber ) - with: ( OptionalConfigurationParameter - named: 'Max Idle Sessions Count' - describedBy: 'Maximum number of idle sessions to keep alive in the pool' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMaxIdleSessionsCount - convertingWith: #asNumber ) - with: ( OptionalConfigurationParameter - named: 'Max Active Sessions Count' - describedBy: 'Maximum number of sessions alive' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMaxActiveSessionsCount - convertingWith: #asNumber ) -] - -{ #category : #private } +{ #category : 'private' } PersistentAPIApplication class >> sectionsForSaganConfiguration [ ^ #( 'Sagan' ) ] -{ #category : #private } +{ #category : 'private' } PersistentAPIApplication class >> serviceDiscoveryHealthCheckIntervalConfigurationParameter [ ^ OptionalConfigurationParameter @@ -84,7 +90,7 @@ PersistentAPIApplication class >> serviceDiscoveryHealthCheckIntervalConfigurati convertingWith: #asNumber ] -{ #category : #private } +{ #category : 'private' } PersistentAPIApplication class >> serviceDiscoveryTimeSlotBetweenRetriesConfigurationParameter [ ^ OptionalConfigurationParameter @@ -95,7 +101,7 @@ PersistentAPIApplication class >> serviceDiscoveryTimeSlotBetweenRetriesConfigur convertingWith: #asNumber ] -{ #category : #private } +{ #category : 'private' } PersistentAPIApplication class >> serviceDiscoveryTimeoutConfigurationParameter [ ^ OptionalConfigurationParameter @@ -107,16 +113,17 @@ PersistentAPIApplication class >> serviceDiscoveryTimeoutConfigurationParameter convertingWith: #asNumber ] -{ #category : #accessing } +{ #category : 'accessing' } PersistentAPIApplication class >> stargateConfigurationParameters [ ^ super stargateConfigurationParameters , { self serviceDiscoveryHealthCheckIntervalConfigurationParameter. self serviceDiscoveryTimeoutConfigurationParameter. - self serviceDiscoveryTimeSlotBetweenRetriesConfigurationParameter } + self serviceDiscoveryTimeSlotBetweenRetriesConfigurationParameter. + self allowedOriginsConfigurationParameter } ] -{ #category : #'private - activation/deactivation' } +{ #category : 'private - activation/deactivation' } PersistentAPIApplication >> basicStartWithin: context [ self installAndStartRootSystem. @@ -124,55 +131,88 @@ PersistentAPIApplication >> basicStartWithin: context [ super basicStartWithin: context ] -{ #category : #'private - activation/deactivation' } +{ #category : 'private - activation/deactivation' } PersistentAPIApplication >> basicStop [ super basicStop. - LaunchpadLogRecord emitInfo: 'Stopping system'. + LogRecord emitInfo: 'Stopping system'. rootSystem ifNotNil: #shutDown ] -{ #category : #'private - activation/deactivation' } +{ #category : 'private - activation/deactivation' } +PersistentAPIApplication >> configureAllowedOriginsTo: api [ + + self stargateConfiguration allowedOrigins value: api. + +] + +{ #category : 'private - activation/deactivation' } +PersistentAPIApplication >> createAPI [ + + | api | + api := super createAPI. + self configureAllowedOriginsTo: api. + ^ api +] + +{ #category : 'private - activation/deactivation' } PersistentAPIApplication >> handleOptionsIn: context [ "I'm a template method. Subclasses can re-implement me to handle options." ] -{ #category : #'private - activation/deactivation' } +{ #category : 'private - activation/deactivation' } PersistentAPIApplication >> installAndStartRootSystem [ - LaunchpadLogRecord emitInfo: 'Installing system'. + LogRecord emitInfo: 'Installing system'. rootSystem := self installation install: self class version. - LaunchpadLogRecord emitInfo: 'Starting up system'. + LogRecord emitInfo: 'Starting up system'. rootSystem startUp ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } PersistentAPIApplication >> installation [ ^ self subclassResponsibility ] -{ #category : #'private - accessing' } +{ #category : 'private - accessing' } PersistentAPIApplication >> saganConfiguration [ ^ self configuration sagan ] -{ #category : #'private - building' } +{ #category : 'private - building' } PersistentAPIApplication >> serviceDiscoveryHealthCheckInterval [ ^ self stargateConfiguration serviceDiscoveryHealthcheckIntervalMs milliSeconds ] -{ #category : #'private - building' } +{ #category : 'private - building' } PersistentAPIApplication >> serviceDiscoveryHealthCheckTimeout [ ^ self stargateConfiguration serviceDiscoveryTimeoutMs milliSeconds ] -{ #category : #'private - building' } +{ #category : 'private - building' } PersistentAPIApplication >> serviceDiscoveryTimeSlotBetweenRetries [ ^ self stargateConfiguration serviceDiscoveryTimeSlotBetweenRetriesInMs milliSeconds ] + +{ #category : 'private - building' } +PersistentAPIApplication >> stackTraceDumpFileReference [ + + ^ self class logsDirectory / ( '<1s>-<2s>' expandMacrosWith: self class commandName + with: ( ( ZTimestampFormat fromString: '2001-02-03_16-05-06.07' ) format: ZTimestamp now ) ) +] + +{ #category : 'private - building' } +PersistentAPIApplication >> stackTraceDumper [ + + ^ CompositeStackTraceDumper composingAll: { + ( StackTraceTextDumper on: [ :dumpAction | + ( self stackTraceDumpFileReference withExtension: 'log' ) writeStreamDo: dumpAction ] ). + ( StackTraceBinarySerializer on: [ :dumpAction | + ( self stackTraceDumpFileReference withExtension: 'fuel' ) binaryWriteStreamDo: dumpAction ] ) } +] diff --git a/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st b/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st index 2da08f8..06742d0 100644 --- a/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st +++ b/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #RDBMSMappingConfiguration } +Extension { #name : 'RDBMSMappingConfiguration' } -{ #category : #'*Persistent-API-Skeleton' } +{ #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> mappingForOptional: anAttributeName to: aFieldName on: aTableName [ | dbToStConversion stToDbConversion | @@ -20,7 +20,7 @@ RDBMSMappingConfiguration >> mappingForOptional: anAttributeName to: aFieldName fromSmalltalkToDatabaseUsing: stToDbConversion ) } ] -{ #category : #'*Persistent-API-Skeleton' } +{ #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> remoteReferenceConversionDefinition [ ^ PluggableMappingConversionDefinition named: 'Remote Reference' @@ -28,7 +28,7 @@ RDBMSMappingConfiguration >> remoteReferenceConversionDefinition [ fromSmalltalkToDatabaseUsing: [ :reference | reference selfLocation asString ] ] -{ #category : #'*Persistent-API-Skeleton' } +{ #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> uuidConversionDefinition [ ^ PluggableMappingConversionDefinition named: 'UUID' diff --git a/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st b/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st new file mode 100644 index 0000000..41df1b0 --- /dev/null +++ b/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st @@ -0,0 +1,92 @@ +Class { + #name : 'SaganParameterDefinitionProvider', + #superclass : 'Object', + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' +} + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> defaultMaxActiveSessionsCount [ + + ^ 5 +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> defaultMaxConnectionAttemps [ + + ^ 3 +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> defaultMaxIdleSessionsCount [ + + ^ 5 +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> defaultMinIdleSessionsCount [ + + ^ 2 +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> defaultTimeSlotBetweenConnectionRetries [ + + ^ Duration milliSeconds: 3000 +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> saganBasicConfigurationParameters [ + + ^ OrderedCollection new + add: ( OptionalConfigurationParameter + named: 'Log Database Events' + describedBy: 'A boolean indicating whether should log database events or not' + inside: self sectionsForSaganConfiguration + defaultingTo: false + convertingWith: #asBoolean ); + add: ( OptionalConfigurationParameter + named: 'Maximum Connection Attemps' + describedBy: 'Number of retries login database attempts' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxConnectionAttemps + convertingWith: #asNumber ); + add: ( OptionalConfigurationParameter + named: 'Time Slot Between Connection Retries In ms' + describedBy: 'Timeframe in milliseconds to wait before retrying a login to database' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultTimeSlotBetweenConnectionRetries + convertingWith: [ :parameter | Duration milliSeconds: parameter asNumber ] ); + yourself +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> saganSessionPoolingConfigurationParameters [ + + ^ OrderedCollection new + add: ( OptionalConfigurationParameter + named: 'Min Idle Sessions Count' + describedBy: 'Minimum number of idle sessions to keep alive in the pool' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMinIdleSessionsCount + convertingWith: #asNumber ); + add: ( OptionalConfigurationParameter + named: 'Max Idle Sessions Count' + describedBy: 'Maximum number of idle sessions to keep alive in the pool' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxIdleSessionsCount + convertingWith: #asNumber ); + add: ( OptionalConfigurationParameter + named: 'Max Active Sessions Count' + describedBy: 'Maximum number of sessions alive' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxActiveSessionsCount + convertingWith: #asNumber ); + yourself +] + +{ #category : 'private' } +SaganParameterDefinitionProvider class >> sectionsForSaganConfiguration [ + + ^ #( 'Sagan' ) +] diff --git a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st index c6f6169..3048af8 100644 --- a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st +++ b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st @@ -1,15 +1,16 @@ Class { - #name : #SingleDatabaseRepositoryProviderModule, - #superclass : #SystemModule, + #name : 'SingleDatabaseRepositoryProviderModule', + #superclass : 'SystemModule', #instVars : [ 'rootSystem', 'login', 'configurationAction' ], - #category : #'Persistent-API-Skeleton' + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } SingleDatabaseRepositoryProviderModule class >> toInstallOn: aCompositeSystem usingSessionPoolWith: aDatabaseLogin configuredBy: aConfigurationAction [ ^ self new @@ -18,7 +19,7 @@ SingleDatabaseRepositoryProviderModule class >> toInstallOn: aCompositeSystem us configuredBy: aConfigurationAction ] -{ #category : #initialization } +{ #category : 'initialization' } SingleDatabaseRepositoryProviderModule >> initializeToInstallOn: aCompositeSystem usingSessionPoolWith: aDatabaseLogin configuredBy: aConfigurationAction [ rootSystem := aCompositeSystem. @@ -26,13 +27,13 @@ SingleDatabaseRepositoryProviderModule >> initializeToInstallOn: aCompositeSyste configurationAction := aConfigurationAction ] -{ #category : #private } +{ #category : 'private' } SingleDatabaseRepositoryProviderModule >> name [ ^'Single Database Repository Provider' ] -{ #category : #private } +{ #category : 'private' } SingleDatabaseRepositoryProviderModule >> registerRepositoryProviderSystemForInstallationIn: systems [ ^ self @@ -46,13 +47,13 @@ SingleDatabaseRepositoryProviderModule >> registerRepositoryProviderSystemForIns in: systems ] -{ #category : #accessing } +{ #category : 'accessing' } SingleDatabaseRepositoryProviderModule >> rootSystem [ ^ rootSystem ] -{ #category : #private } +{ #category : 'private' } SingleDatabaseRepositoryProviderModule >> systemInterfacesToInstall [ ^#(RepositoryProviderSystem) diff --git a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st index 5a7f1b9..06dc6d5 100644 --- a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st @@ -1,56 +1,56 @@ Class { - #name : #SingleDatabaseRepositoryProviderModuleFactory, - #superclass : #Object, + #name : 'SingleDatabaseRepositoryProviderModuleFactory', + #superclass : 'Object', #instVars : [ - 'databaseConfiguration', - 'configurationAction' + 'databaseConfiguration' ], - #category : #'Persistent-API-Skeleton' + #category : 'Persistent-API-Skeleton', + #package : 'Persistent-API-Skeleton' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } SingleDatabaseRepositoryProviderModuleFactory class >> configuredBy: theDatabaseConfiguration [ - ^ self configuredBy: theDatabaseConfiguration withPoolingOptions: [ :options | ] + ^ self new initializeConfiguredBy: theDatabaseConfiguration ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } SingleDatabaseRepositoryProviderModuleFactory class >> configuredBy: theDatabaseConfiguration withPoolingOptions: poolingOptionsConfigurationAction [ - ^ self new initializeConfiguredBy: theDatabaseConfiguration withPoolingOptions: [ :options | - theDatabaseConfiguration keysAndValuesDo: [ :key :value | options at: key ifAbsentPut: value ]. - poolingOptionsConfigurationAction value: options - ] + | options | + options := NeoJSONObject new. + poolingOptionsConfigurationAction value: options. + theDatabaseConfiguration keysAndValuesDo: [ :key :value | options at: key ifAbsentPut: value ]. + self deprecated: 'Use configuredBy:, passing all keys and values to theDatabaseConfiguration'. + ^ self configuredBy: options ] -{ #category : #testing } +{ #category : 'testing' } SingleDatabaseRepositoryProviderModuleFactory class >> isAbstract [ ^self = SingleDatabaseRepositoryProviderModuleFactory ] -{ #category : #private } +{ #category : 'private' } SingleDatabaseRepositoryProviderModuleFactory >> databaseLogin [ self subclassResponsibility ] -{ #category : #initialization } -SingleDatabaseRepositoryProviderModuleFactory >> initializeConfiguredBy: theDatabaseConfiguration - withPoolingOptions: poolingOptionsConfigurationAction [ +{ #category : 'initialization' } +SingleDatabaseRepositoryProviderModuleFactory >> initializeConfiguredBy: theDatabaseConfiguration [ databaseConfiguration := theDatabaseConfiguration. - configurationAction := poolingOptionsConfigurationAction ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } SingleDatabaseRepositoryProviderModuleFactory >> toInstallOn: aCompositeSystem [ - | login | - - login := self databaseLogin. - ^ SingleDatabaseRepositoryProviderModule - toInstallOn: aCompositeSystem - usingSessionPoolWith: login - configuredBy: configurationAction + | login | + login := self databaseLogin. + ^ SingleDatabaseRepositoryProviderModule + toInstallOn: aCompositeSystem + usingSessionPoolWith: login + configuredBy: [ :options | + databaseConfiguration keysAndValuesDo: [ :key :value | options at: key put: value ] ] ] diff --git a/source/Persistent-API-Skeleton/package.st b/source/Persistent-API-Skeleton/package.st index 4191cb0..f97a588 100644 --- a/source/Persistent-API-Skeleton/package.st +++ b/source/Persistent-API-Skeleton/package.st @@ -1 +1 @@ -Package { #name : #'Persistent-API-Skeleton' } +Package { #name : 'Persistent-API-Skeleton' } From f54c4a66fa99fd439bbce4548d6d977287852abb Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Tue, 15 Jul 2025 10:41:37 -0300 Subject: [PATCH 2/5] Fix test --- ...letonHTTPClientErrorExtensionsTest.class.st | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st index 3074311..5caeecb 100644 --- a/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st @@ -1,13 +1,11 @@ Class { - #name : #APISkeletonHTTPClientErrorExtensionsTest, - #superclass : #TestCase, - #pools : [ - 'JRPCConstantsSharedPool' - ], - #category : #'API-Skeleton-Tests' + #name : 'APISkeletonHTTPClientErrorExtensionsTest', + #superclass : 'TestCase', + #category : 'API-Skeleton-Tests', + #package : 'API-Skeleton-Tests' } -{ #category : #tests } +{ #category : 'tests' } APISkeletonHTTPClientErrorExtensionsTest >> testHTTPClientErrorAsJRPCResponse [ self @@ -19,7 +17,7 @@ APISkeletonHTTPClientErrorExtensionsTest >> testHTTPClientErrorAsJRPCResponse [ response := error asJRPCResponse. self assert: response isError; - assert: response error code equals: InternalError; + assert: response error code equals: -32603; assert: response error message equals: 'Internal JSON-RPC error.'; assert: (response error data at: 'errorClass') equals: 'HTTPClientError'; assert: (response error data at: 'messageText') equals: 'Bad Request'; @@ -27,7 +25,7 @@ APISkeletonHTTPClientErrorExtensionsTest >> testHTTPClientErrorAsJRPCResponse [ ] ] -{ #category : #tests } +{ #category : 'tests' } APISkeletonHTTPClientErrorExtensionsTest >> testUnprocessableEntityAsJRPCResponse [ self @@ -39,7 +37,7 @@ APISkeletonHTTPClientErrorExtensionsTest >> testUnprocessableEntityAsJRPCRespons response := error asJRPCResponse. self assert: response isError; - assert: response error code equals: InvalidParams; + assert: response error code equals: -32602; assert: response error message equals: 'Invalid parameter format' ] ] From 35b614a118780c5f0326add1863c62f5d4dcb9e0 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Tue, 15 Jul 2025 11:30:37 -0300 Subject: [PATCH 3/5] Fix formatting and improve some tests --- ...ISkeletonCollectionExtensionsTest.class.st | 4 +- ...etonHTTPClientErrorExtensionsTest.class.st | 52 ++-- .../APISkeletonZnUrlExtensionsTest.class.st | 6 +- .../NeoJSONReaderExtensionsTest.class.st | 140 +++++----- .../NeoJSONWriterExtensionsTest.class.st | 24 +- .../RemoteReferenceTest.class.st | 22 +- source/API-Skeleton/Collection.extension.st | 5 +- .../API-Skeleton/HTTPClientError.extension.st | 22 +- .../API-Skeleton/NeoJSONReader.extension.st | 69 +++-- .../NeoJSONStrictObjectMapping.class.st | 85 +++--- .../API-Skeleton/NeoJSONWriter.extension.st | 6 +- source/API-Skeleton/RemoteReference.class.st | 12 +- source/API-Skeleton/ZnUrl.extension.st | 2 +- .../BaselineOfPersistentAPISkeleton.class.st | 92 ++++--- ...anParameterDefinitionProvider.extension.st | 50 ++-- ...eSQLDatabaseProviderModuleFactory.class.st | 27 +- ...anParameterDefinitionProvider.extension.st | 18 +- ...LiteDatabaseProviderModuleFactory.class.st | 18 +- .../ConfigureAllowedOriginsTest.class.st | 14 +- .../CreateEmptyRDBMSApplicationTest.class.st | 4 +- .../PersistentPetStoreApplication.class.st | 24 +- ...PersistentPetStoreApplicationTest.class.st | 259 +++++++++--------- .../PetStoreInstallation.class.st | 19 +- .../RDBMSMappingConfigurationTest.class.st | 80 +++--- ...DatabaseProviderModuleFactoryTest.class.st | 32 +-- ...DatabaseProviderModuleFactoryTest.class.st | 48 ++-- .../CompositeStackTraceDumper.class.st | 8 +- .../CreateEmptyRDBMSApplication.class.st | 45 ++- .../PersistentAPIApplication.class.st | 63 ++--- .../RDBMSMappingConfiguration.extension.st | 46 ++-- .../SaganParameterDefinitionProvider.class.st | 78 +++--- ...eDatabaseRepositoryProviderModule.class.st | 40 +-- ...seRepositoryProviderModuleFactory.class.st | 8 +- 33 files changed, 693 insertions(+), 729 deletions(-) diff --git a/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st index 029e957..edd4862 100644 --- a/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonCollectionExtensionsTest.class.st @@ -8,11 +8,11 @@ Class { { #category : 'tests' } APISkeletonCollectionExtensionsTest >> testAssociationArrayAsNeoJSON [ - self assert: { 'a' -> 'b' } asNeoJSON a equals: 'b' + self assert: { 'a' -> 'b' } asNeoJSON a equals: 'b' ] { #category : 'tests' } APISkeletonCollectionExtensionsTest >> testDictionaryAsNeoJSON [ - self assert: { #key -> 'value' } asDictionary asNeoJSON key equals: 'value' + self assert: { #key -> 'value' } asDictionary asNeoJSON key equals: 'value' ] diff --git a/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st index 5caeecb..2aca0e9 100644 --- a/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonHTTPClientErrorExtensionsTest.class.st @@ -8,36 +8,34 @@ Class { { #category : 'tests' } APISkeletonHTTPClientErrorExtensionsTest >> testHTTPClientErrorAsJRPCResponse [ - self - should: [ HTTPClientError badRequest signal: 'Bad Request' ] - raise: HTTPClientError badRequest - withExceptionDo: [ :error | - | response | - - response := error asJRPCResponse. - self - assert: response isError; - assert: response error code equals: -32603; - assert: response error message equals: 'Internal JSON-RPC error.'; - assert: (response error data at: 'errorClass') equals: 'HTTPClientError'; - assert: (response error data at: 'messageText') equals: 'Bad Request'; - assert: (response error data at: 'code') equals: '400' - ] + self + should: [ HTTPClientError badRequest signal: 'Bad Request' ] + raise: HTTPClientError badRequest + withExceptionDo: [ :error | + | response | + response := error asJRPCResponse. + self + assert: response isError; + assert: response error code equals: JRPCConstants internalErrorCode; + assert: response error message equals: 'Internal JSON-RPC error.'; + assert: ( response error data at: 'errorClass' ) equals: 'HTTPClientError'; + assert: ( response error data at: 'messageText' ) equals: 'Bad Request'; + assert: ( response error data at: 'code' ) equals: '400' + ] ] { #category : 'tests' } APISkeletonHTTPClientErrorExtensionsTest >> testUnprocessableEntityAsJRPCResponse [ - self - should: [ HTTPClientError unprocessableEntity signal: 'Invalid parameter format' ] - raise: HTTPClientError unprocessableEntity - withExceptionDo: [ :error | - | response | - - response := error asJRPCResponse. - self - assert: response isError; - assert: response error code equals: -32602; - assert: response error message equals: 'Invalid parameter format' - ] + self + should: [ HTTPClientError unprocessableEntity signal: 'Invalid parameter format' ] + raise: HTTPClientError unprocessableEntity + withExceptionDo: [ :error | + | response | + response := error asJRPCResponse. + self + assert: response isError; + assert: response error code equals: JRPCConstants invalidParametersErrorCode; + assert: response error message equals: 'Invalid parameter format' + ] ] diff --git a/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st b/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st index fed2bcf..861e5fe 100644 --- a/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/APISkeletonZnUrlExtensionsTest.class.st @@ -8,7 +8,7 @@ Class { { #category : 'tests' } APISkeletonZnUrlExtensionsTest >> testNotEmpty [ - self - assert: 'http://google.com' asUrl notEmpty; - deny: '' asUrl notEmpty + self + assert: 'http://google.com' asUrl notEmpty; + deny: '' asUrl notEmpty ] diff --git a/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st b/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st index 875ded5..4fb5368 100644 --- a/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/NeoJSONReaderExtensionsTest.class.st @@ -8,129 +8,121 @@ Class { { #category : 'private' } NeoJSONReaderExtensionsTest >> readAssociationFrom: string [ - | reader | - - reader := NeoJSONReader on: string readStream. - reader - for: Association - strictDo: [ :mapping | - mapping - mapInstVar: #key; - mapInstVar: #value - ]. - - ^ reader nextAs: Association + | reader | + reader := NeoJSONReader on: string readStream. + reader for: Association strictDo: [ :mapping | + mapping + mapInstVar: #key; + mapInstVar: #value + ]. + + ^ reader nextAs: Association ] { #category : 'private' } NeoJSONReaderExtensionsTest >> readRemoteReferenceFrom: aJSONString [ - | reader | - - reader := NeoJSONReader on: aJSONString readStream. - reader - mapRemoteReference; - for: Association - do: [ :mapping | - mapping mapProperty: #rel setter: [ :association :key | association key: key ]. - ( mapping - mapProperty: #href - setter: [ :association :selfLocation | association value: selfLocation ] ) - valueSchema: RemoteReference - ]. - - ^ reader nextAs: Association + | reader | + reader := NeoJSONReader on: aJSONString readStream. + reader + mapRemoteReference; + for: Association do: [ :mapping | + mapping mapProperty: #rel setter: [ :association :key | association key: key ]. + ( mapping + mapProperty: #href + setter: [ :association :selfLocation | association value: selfLocation ] ) valueSchema: + RemoteReference + ]. + + ^ reader nextAs: Association ] { #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromEmptyURL [ - self - should: [ self readRemoteReferenceFrom: '{"rel":"self","href":""}' ] - raise: NeoJSONParseError - withMessageText: 'An empty location is not a valid URI' + self + should: [ self readRemoteReferenceFrom: '{"rel":"self","href":""}' ] + raise: NeoJSONParseError + withMessageText: 'An empty location is not a valid URI' ] { #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromInvalidURL [ - self - should: [ self readRemoteReferenceFrom: '{"rel":"self","href":"http://foo:8080/foo?%%bar=1"}' ] - raise: NeoJSONParseError - withMessageText: 'Invalid URI'; - should: [ self readRemoteReferenceFrom: '{"rel":"self","href":"http://foo:bad/resource"}' ] - raise: NeoJSONParseError - withMessageText: 'Invalid URI' + self + should: [ self readRemoteReferenceFrom: '{"rel":"self","href":"http://foo:8080/foo?%%bar=1"}' ] + raise: NeoJSONParseError + withMessageText: 'Invalid URI'; + should: [ self readRemoteReferenceFrom: '{"rel":"self","href":"http://foo:bad/resource"}' ] + raise: NeoJSONParseError + withMessageText: 'Invalid URI' ] { #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testCantParseRemoteReferenceFromNonStringObject [ - self - should: [ self readRemoteReferenceFrom: '{"rel":"self","href":{}}' ] - raise: NeoJSONParseError - withMessageText: '" expected' + self + should: [ self readRemoteReferenceFrom: '{"rel":"self","href":{}}' ] + raise: NeoJSONParseError + withMessageText: '" expected' ] { #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfKeyAndValuePropertiesAreMissing [ - self - should: [ self readAssociationFrom: '{}' ] - raise: NeoJSONParseError - withMessageText: 'Missing required keys (#key, #value)' + self + should: [ self readAssociationFrom: '{}' ] + raise: NeoJSONParseError + withMessageText: 'Missing required keys (#key, #value)' ] { #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfKeyPropertyIsMissing [ - self - should: [ self readAssociationFrom: '{"value":1}' ] - raise: NeoJSONParseError - withMessageText: 'Missing required keys (#key)' + self + should: [ self readAssociationFrom: '{"value":1}' ] + raise: NeoJSONParseError + withMessageText: 'Missing required keys (#key)' ] { #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testFailIfValuePropertyIsMissing [ - self - should: [ self readAssociationFrom: '{"key":"one"}' ] - raise: NeoJSONParseError - withMessageText: 'Missing required keys (#value)' + self + should: [ self readAssociationFrom: '{"key":"one"}' ] + raise: NeoJSONParseError + withMessageText: 'Missing required keys (#value)' ] { #category : 'tests - remote reference' } NeoJSONReaderExtensionsTest >> testParseRemoteReference [ - | object | + | object | + object := self readRemoteReferenceFrom: + '{"rel":"self","href":"https://api.example.com/resource/00000000-0000-0000-0000-000000000000"}'. - object := self - readRemoteReferenceFrom: '{"rel":"self","href":"https://api.example.com/resource/00000000-0000-0000-0000-000000000000"}'. - - self - assert: object key equals: 'self'; - assert: object value selfLocation - equals: 'https://api.example.com/resource/00000000-0000-0000-0000-000000000000' + self + assert: object key equals: 'self'; + assert: object value selfLocation + equals: 'https://api.example.com/resource/00000000-0000-0000-0000-000000000000' ] { #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testParsingValidObject [ - | object | - - object := self readAssociationFrom: '{"key":"one","value":1}'. - self - assert: object key equals: 'one'; - assert: object value equals: 1 + | object | + object := self readAssociationFrom: '{"key":"one","value":1}'. + self + assert: object key equals: 'one'; + assert: object value equals: 1 ] { #category : 'tests - strict mode' } NeoJSONReaderExtensionsTest >> testParsingValidObjectWithExtraFields [ - | object | - - object := self readAssociationFrom: '{"key":"one","value":1,"garbage":8}'. - self - assert: object key equals: 'one'; - assert: object value equals: 1 + | object | + object := self readAssociationFrom: '{"key":"one","value":1,"garbage":8}'. + self + assert: object key equals: 'one'; + assert: object value equals: 1 ] diff --git a/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st b/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st index e59ee60..9db5308 100644 --- a/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st +++ b/source/API-Skeleton-Tests/NeoJSONWriterExtensionsTest.class.st @@ -8,17 +8,17 @@ Class { { #category : 'tests' } NeoJSONWriterExtensionsTest >> testWriteRemoteReference [ - | object json | + | object json | + object := RemoteReference locatedAt: + 'https://api.example.com/resource/00000000-0000-0000-0000-000000000000' asUrl. + json := String streamContents: [ :stream | + ( NeoJSONWriter on: stream ) + prettyPrint: true; + mapRemoteReference; + nextPut: object + ]. - object := RemoteReference - locatedAt: 'https://api.example.com/resource/00000000-0000-0000-0000-000000000000' asUrl. - json := String - streamContents: [ :stream | - ( NeoJSONWriter on: stream ) - prettyPrint: true; - mapRemoteReference; - nextPut: object - ]. - - self assert: json equals: '"https://api.example.com/resource/00000000-0000-0000-0000-000000000000"' + self + assert: json + equals: '"https://api.example.com/resource/00000000-0000-0000-0000-000000000000"' ] diff --git a/source/API-Skeleton-Tests/RemoteReferenceTest.class.st b/source/API-Skeleton-Tests/RemoteReferenceTest.class.st index 637dc66..981f1d6 100644 --- a/source/API-Skeleton-Tests/RemoteReferenceTest.class.st +++ b/source/API-Skeleton-Tests/RemoteReferenceTest.class.st @@ -11,21 +11,21 @@ Class { { #category : 'tests' } RemoteReferenceTest >> testAccessing [ - self - assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) selfLocation - equals: 'https://google.com/' + self + assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) selfLocation + equals: 'https://google.com/' ] { #category : 'tests' } RemoteReferenceTest >> testComparing [ - self - assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) - equals: ( RemoteReference locatedAt: 'https://google.com/' asUrl ); - assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) hash - equals: ( RemoteReference locatedAt: 'https://google.com/' asUrl ) hash. + self + assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) + equals: ( RemoteReference locatedAt: 'https://google.com/' asUrl ); + assert: ( RemoteReference locatedAt: 'https://google.com' asUrl ) hash + equals: ( RemoteReference locatedAt: 'https://google.com/' asUrl ) hash. - self - deny: ( RemoteReference locatedAt: 'http://google.com' asUrl ) - equals: ( RemoteReference locatedAt: 'https://google.com' asUrl ) + self + deny: ( RemoteReference locatedAt: 'http://google.com' asUrl ) + equals: ( RemoteReference locatedAt: 'https://google.com' asUrl ) ] diff --git a/source/API-Skeleton/Collection.extension.st b/source/API-Skeleton/Collection.extension.st index 70b3ded..f0142ae 100644 --- a/source/API-Skeleton/Collection.extension.st +++ b/source/API-Skeleton/Collection.extension.st @@ -2,8 +2,7 @@ Extension { #name : 'Collection' } { #category : '*API-Skeleton' } Collection >> asNeoJSON [ + "This method is intended to work on dictionaries or collections of associations" - "This method is intended to work on dictionaries or collections of associations" - - ^ self as: NeoJSONObject + ^ self as: NeoJSONObject ] diff --git a/source/API-Skeleton/HTTPClientError.extension.st b/source/API-Skeleton/HTTPClientError.extension.st index 81b6a93..54bbeed 100644 --- a/source/API-Skeleton/HTTPClientError.extension.st +++ b/source/API-Skeleton/HTTPClientError.extension.st @@ -3,20 +3,20 @@ Extension { #name : 'HTTPClientError' } { #category : '*API-Skeleton' } HTTPClientError >> asJRPCJSON [ - ^ super asJRPCJSON - at: 'code' put: self code asString; - yourself + ^ super asJRPCJSON + at: 'code' put: self code asString; + yourself ] { #category : '*API-Skeleton' } HTTPClientError >> asJRPCResponseWithId: anInteger [ - ^ self code = 422 - then: [ - | response | - response := JRPCErrorResponseObject id: anInteger error: JRPCErrorObject invalidParams. - response error message: self messageText. - response - ] - otherwise: [ super asJRPCResponseWithId: anInteger ] + ^ self code = 422 + then: [ + | response | + response := JRPCErrorResponseObject id: anInteger error: JRPCErrorObject invalidParams. + response error message: self messageText. + response + ] + otherwise: [ super asJRPCResponseWithId: anInteger ] ] diff --git a/source/API-Skeleton/NeoJSONReader.extension.st b/source/API-Skeleton/NeoJSONReader.extension.st index 887d73e..83505a7 100644 --- a/source/API-Skeleton/NeoJSONReader.extension.st +++ b/source/API-Skeleton/NeoJSONReader.extension.st @@ -1,49 +1,44 @@ -Extension { #name : #NeoJSONReader } +Extension { #name : 'NeoJSONReader' } -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } NeoJSONReader >> for: schemaName strictDo: block [ - | mapping | - - mapping := self strictMappingFor: schemaName. - block value: mapping. - ^ mapping + | mapping | + mapping := self strictMappingFor: schemaName. + block value: mapping. + ^ mapping ] -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } NeoJSONReader >> mapRemoteReference [ - self - for: RemoteReference - customDo: [ :mapping | - mapping - decoder: [ :location | - AssertionCheckerBuilder new - raising: NeoJSONParseError; - checking: [ :asserter | - asserter - enforce: [ location isString ] - because: '" expected' - onSuccess: - [ :successAsserter | successAsserter enforce: [ location notEmpty ] because: 'An empty location is not a valid URI' ] - ]; - buildAndCheck. - RemoteReference - locatedAt: - ( [ location asUrl ] - on: ZnCharacterEncodingError , ZnPortNotANumber - do: [ :error | self error: 'Invalid URI' ] ) - ] - ] + self for: RemoteReference customDo: [ :mapping | + mapping decoder: [ :location | + AssertionCheckerBuilder new + raising: NeoJSONParseError; + checking: [ :asserter | + asserter + enforce: [ location isString ] + because: '" expected' + onSuccess: [ :successAsserter | + successAsserter + enforce: [ location notEmpty ] + because: 'An empty location is not a valid URI' ] + ]; + buildAndCheck. + RemoteReference locatedAt: ( [ location asUrl ] + on: ZnCharacterEncodingError , ZnPortNotANumber + do: [ :error | self error: 'Invalid URI' ] ) + ] + ] ] -{ #category : #'*API-Skeleton' } +{ #category : '*API-Skeleton' } NeoJSONReader >> strictMappingFor: smalltalkClass [ - ^ self mappings - at: smalltalkClass - ifAbsentPut: [ NeoJSONStrictObjectMapping new - subjectClass: smalltalkClass; - yourself - ] + ^ self mappings at: smalltalkClass ifAbsentPut: [ + NeoJSONStrictObjectMapping new + subjectClass: smalltalkClass; + yourself + ] ] diff --git a/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st b/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st index facd0de..8201f96 100644 --- a/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st +++ b/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st @@ -3,55 +3,54 @@ I am NeoJSONStrictObjectMapping, I'm equivalent to NeoJSONObjectMapping but more I will fail on reading properties of an object if some of the mapped properties are missing in the incoming JSON. " Class { - #name : #NeoJSONStrictObjectMapping, - #superclass : #NeoJSONObjectMapping, - #category : #'API-Skeleton' + #name : 'NeoJSONStrictObjectMapping', + #superclass : 'NeoJSONObjectMapping', + #category : 'API-Skeleton', + #package : 'API-Skeleton' } -{ #category : #private } +{ #category : 'private' } NeoJSONStrictObjectMapping >> errorDescriptionForMissing: propertyNames [ - ^ String - streamContents: [ :stream | - stream - nextPutAll: 'Missing required keys'; - space; - nextPut: $(. - propertyNames - do: [ :propertyName | - stream - nextPut: $#; - nextPutAll: propertyName - ] - separatedBy: [ stream - nextPut: $,; - space - ]. - stream nextPut: $) - ] + ^ String streamContents: [ :stream | + stream + nextPutAll: 'Missing required keys'; + space; + nextPut: $(. + propertyNames + do: [ :propertyName | + stream + nextPut: $#; + nextPutAll: propertyName + ] + separatedBy: [ + stream + nextPut: $,; + space + ]. + stream nextPut: $) + ] ] -{ #category : #parsing } +{ #category : 'parsing' } NeoJSONStrictObjectMapping >> readFrom: jsonReader [ - | anObject propertyNames | - - anObject := subjectClass new. - allowNil - ifTrue: [ jsonReader - parseConstantDo: [ :value | ^ value ifNotNil: [ jsonReader error: 'Unexpected boolean constant' ] ] - ]. - propertyNames := properties collect: [ :each | each propertyName ]. - jsonReader - parseMapKeysDo: [ :key | - ( self propertyNamed: key ifAbsent: [ nil ] ) - ifNil: [ "read, skip & ignore value" jsonReader next ] - ifNotNil: [ :mapping | - propertyNames remove: key asSymbol. - mapping readObject: anObject from: jsonReader - ] - ]. - propertyNames ifNotEmpty: [ jsonReader error: ( self errorDescriptionForMissing: propertyNames ) ]. - ( anObject respondsTo: #assertIsValid ) then: [ anObject assertIsValid ]. - ^ anObject + | anObject propertyNames | + anObject := subjectClass new. + allowNil ifTrue: [ + jsonReader parseConstantDo: [ :value | + ^ value ifNotNil: [ jsonReader error: 'Unexpected boolean constant' ] ] + ]. + propertyNames := properties collect: [ :each | each propertyName ]. + jsonReader parseMapKeysDo: [ :key | + ( self propertyNamed: key ifAbsent: [ nil ] ) + ifNil: [ "read, skip & ignore value" jsonReader next ] + ifNotNil: [ :mapping | + propertyNames remove: key asSymbol. + mapping readObject: anObject from: jsonReader + ] + ]. + propertyNames ifNotEmpty: [ jsonReader error: ( self errorDescriptionForMissing: propertyNames ) ]. + ( anObject respondsTo: #assertIsValid ) then: [ anObject assertIsValid ]. + ^ anObject ] diff --git a/source/API-Skeleton/NeoJSONWriter.extension.st b/source/API-Skeleton/NeoJSONWriter.extension.st index 19cadf2..e370977 100644 --- a/source/API-Skeleton/NeoJSONWriter.extension.st +++ b/source/API-Skeleton/NeoJSONWriter.extension.st @@ -3,7 +3,7 @@ Extension { #name : 'NeoJSONWriter' } { #category : '*API-Skeleton' } NeoJSONWriter >> mapRemoteReference [ - self - for: RemoteReference - customDo: [ :mapping | mapping encoder: [ :remoteReference | remoteReference selfLocation ] ] + self + for: RemoteReference + customDo: [ :mapping | mapping encoder: [ :remoteReference | remoteReference selfLocation ] ] ] diff --git a/source/API-Skeleton/RemoteReference.class.st b/source/API-Skeleton/RemoteReference.class.st index 41ef1f3..3dcb051 100644 --- a/source/API-Skeleton/RemoteReference.class.st +++ b/source/API-Skeleton/RemoteReference.class.st @@ -17,25 +17,25 @@ RemoteReference class >> locatedAt: aUrl [ { #category : 'comparing' } RemoteReference >> = anObject [ - ^ self equalityChecker - compare: #selfLocation; - checkAgainst: anObject + ^ self equalityChecker + compare: #selfLocation; + checkAgainst: anObject ] { #category : 'comparing' } RemoteReference >> hash [ - ^ self selfLocation hash + ^ self selfLocation hash ] { #category : 'initialization' } RemoteReference >> initializeLocatedAt: aUrl [ - selfLocation := aUrl asString + selfLocation := aUrl asString ] { #category : 'initialization' } RemoteReference >> selfLocation [ - ^ selfLocation + ^ selfLocation ] diff --git a/source/API-Skeleton/ZnUrl.extension.st b/source/API-Skeleton/ZnUrl.extension.st index b7fad05..cee40eb 100644 --- a/source/API-Skeleton/ZnUrl.extension.st +++ b/source/API-Skeleton/ZnUrl.extension.st @@ -3,5 +3,5 @@ Extension { #name : 'ZnUrl' } { #category : '*API-Skeleton' } ZnUrl >> notEmpty [ - ^ self isEmpty not + ^ self isEmpty not ] diff --git a/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st b/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st index 535a2df..b7e9e28 100644 --- a/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st +++ b/source/BaselineOfPersistentAPISkeleton/BaselineOfPersistentAPISkeleton.class.st @@ -8,25 +8,25 @@ Class { { #category : 'baselines' } BaselineOfPersistentAPISkeleton >> baseline: spec [ - - spec - for: #pharo - do: [ self - setUpDependencies: spec; - setUpPackages: spec. - spec - group: 'Deployment' with: #('SQLite3 Persistence' 'PostgreSQL Persistence'); - group: 'CI' with: 'Tests'; - group: 'Dependent-SUnit-Extensions' with: #('Stargate-SUnit' 'Kepler-SUnit' 'Launchpad-SUnit'); - group: 'Tools' with: 'Stargate-Tools'; - group: 'Development' with: #('Tests' 'Tools') - ] + + spec for: #pharo do: [ + self + setUpDependencies: spec; + setUpPackages: spec. + spec + group: 'Deployment' with: #( 'SQLite3 Persistence' 'PostgreSQL Persistence' ); + group: 'CI' with: 'Tests'; + group: 'Dependent-SUnit-Extensions' + with: #( 'Stargate-SUnit' 'Kepler-SUnit' 'Launchpad-SUnit' ); + group: 'Tools' with: 'Stargate-Tools'; + group: 'Development' with: #( 'Tests' 'Tools' ) + ] ] { #category : 'accessing' } BaselineOfPersistentAPISkeleton >> projectClass [ - ^ MetacelloCypressBaselineProject + ^ MetacelloCypressBaselineProject ] { #category : 'baselines' } @@ -55,7 +55,9 @@ BaselineOfPersistentAPISkeleton >> setUpDependencies: spec [ spec baseline: 'Launchpad' with: [ spec repository: 'github://ba-st/Launchpad:v7' ]; project: 'Launchpad-Deployment' copyFrom: 'Launchpad' with: [ spec loads: 'Deployment' ]; - project: 'Launchpad-SUnit' copyFrom: 'Launchpad' with: [ spec loads: 'Dependent-SUnit-Extensions' ]. + project: 'Launchpad-SUnit' + copyFrom: 'Launchpad' + with: [ spec loads: 'Dependent-SUnit-Extensions' ]. spec baseline: 'Kepler' with: [ spec repository: 'github://ba-st/Kepler:v7' ]; @@ -66,34 +68,34 @@ BaselineOfPersistentAPISkeleton >> setUpDependencies: spec [ { #category : 'baselines' } BaselineOfPersistentAPISkeleton >> setUpPackages: spec [ - spec - package: 'API-Skeleton' - with: [ spec - requires: #('Stargate-Deployment' 'Kepler-Core' 'Launchpad-Deployment' 'Stargate-Consul-Deployment') - ]; - group: 'Core' with: 'API-Skeleton'; - group: 'Deployment' with: 'API-Skeleton'. - - spec package: 'Persistent-API-Skeleton' with: [ spec requires: 'API-Skeleton' ]. - - spec - package: 'Persistent-API-Skeleton-SQLite' - with: [ spec requires: #('Persistent-API-Skeleton' 'Sagan-SQLite') ]; - group: 'SQLite3 Persistence' with: 'Persistent-API-Skeleton-SQLite'. - - spec - package: 'Persistent-API-Skeleton-PostgreSQL' - with: [ spec requires: #('Persistent-API-Skeleton' 'Sagan-PostgreSQL') ]; - group: 'PostgreSQL Persistence' with: 'Persistent-API-Skeleton-PostgreSQL'. - - spec - package: 'API-Skeleton-Tests' with: [ spec requires: #('API-Skeleton' 'Stargate-SUnit') ]; - group: 'Tests' with: 'API-Skeleton-Tests'. - - spec - package: 'Persistent-API-Skeleton-Tests' - with: [ spec - requires: #('Persistent-API-Skeleton-SQLite' 'Persistent-API-Skeleton-PostgreSQL' 'Stargate-SUnit' 'Stargate-Examples' 'Launchpad-SUnit') - ]; - group: 'Tests' with: 'Persistent-API-Skeleton-Tests' + spec + package: 'API-Skeleton' with: [ + spec requires: + #( 'Stargate-Deployment' 'Kepler-Core' 'Launchpad-Deployment' 'Stargate-Consul-Deployment' ) + ]; + group: 'Core' with: 'API-Skeleton'; + group: 'Deployment' with: 'API-Skeleton'. + + spec package: 'Persistent-API-Skeleton' with: [ spec requires: 'API-Skeleton' ]. + + spec + package: 'Persistent-API-Skeleton-SQLite' + with: [ spec requires: #( 'Persistent-API-Skeleton' 'Sagan-SQLite' ) ]; + group: 'SQLite3 Persistence' with: 'Persistent-API-Skeleton-SQLite'. + + spec + package: 'Persistent-API-Skeleton-PostgreSQL' + with: [ spec requires: #( 'Persistent-API-Skeleton' 'Sagan-PostgreSQL' ) ]; + group: 'PostgreSQL Persistence' with: 'Persistent-API-Skeleton-PostgreSQL'. + + spec + package: 'API-Skeleton-Tests' with: [ spec requires: #( 'API-Skeleton' 'Stargate-SUnit' ) ]; + group: 'Tests' with: 'API-Skeleton-Tests'. + + spec + package: 'Persistent-API-Skeleton-Tests' with: [ + spec requires: #( 'Persistent-API-Skeleton-SQLite' 'Persistent-API-Skeleton-PostgreSQL' + 'Stargate-SUnit' 'Stargate-Examples' 'Launchpad-SUnit' ) + ]; + group: 'Tests' with: 'Persistent-API-Skeleton-Tests' ] diff --git a/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st b/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st index 6cb61b1..0b94755 100644 --- a/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st +++ b/source/Persistent-API-Skeleton-PostgreSQL/SaganParameterDefinitionProvider.extension.st @@ -3,29 +3,29 @@ Extension { #name : 'SaganParameterDefinitionProvider' } { #category : '*Persistent-API-Skeleton-PostgreSQL' } SaganParameterDefinitionProvider class >> saganConfigurationParametersForPostgreSQL [ - ^ OrderedCollection new - add: ( MandatoryConfigurationParameter - named: 'PG Hostname' - describedBy: 'Name of PostgreSQL''s host to connect' - inside: self sectionsForSaganConfiguration ); - add: ( OptionalConfigurationParameter - named: 'PG Port' - describedBy: 'Port number to connect to at the server host' - inside: self sectionsForSaganConfiguration - defaultingTo: 5432 ); - add: ( MandatoryConfigurationParameter - named: 'PG Username' - describedBy: 'PostgreSQL user name to connect as' - inside: self sectionsForSaganConfiguration ); - add: ( MandatoryConfigurationParameter - named: 'PG Password' - describedBy: 'Password to be used if the server demands password authentication' - inside: self sectionsForSaganConfiguration ) asSensitive; - add: ( MandatoryConfigurationParameter - named: 'PG Database Name' - describedBy: 'The database name' - inside: self sectionsForSaganConfiguration ); - addAll: self saganBasicConfigurationParameters; - addAll: self saganSessionPoolingConfigurationParameters; - asArray + ^ OrderedCollection new + add: ( MandatoryConfigurationParameter + named: 'PG Hostname' + describedBy: 'Name of PostgreSQL''s host to connect' + inside: self sectionsForSaganConfiguration ); + add: ( OptionalConfigurationParameter + named: 'PG Port' + describedBy: 'Port number to connect to at the server host' + inside: self sectionsForSaganConfiguration + defaultingTo: 5432 ); + add: ( MandatoryConfigurationParameter + named: 'PG Username' + describedBy: 'PostgreSQL user name to connect as' + inside: self sectionsForSaganConfiguration ); + add: ( MandatoryConfigurationParameter + named: 'PG Password' + describedBy: 'Password to be used if the server demands password authentication' + inside: self sectionsForSaganConfiguration ) asSensitive; + add: ( MandatoryConfigurationParameter + named: 'PG Database Name' + describedBy: 'The database name' + inside: self sectionsForSaganConfiguration ); + addAll: self saganBasicConfigurationParameters; + addAll: self saganSessionPoolingConfigurationParameters; + asArray ] diff --git a/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st b/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st index 3d288db..f50fc77 100644 --- a/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton-PostgreSQL/SinglePostgreSQLDatabaseProviderModuleFactory.class.st @@ -5,20 +5,19 @@ Class { #package : 'Persistent-API-Skeleton-PostgreSQL' } -{ #category : 'instance creation' } +{ #category : 'private' } SinglePostgreSQLDatabaseProviderModuleFactory >> databaseLogin [ - | login | - - DatabaseAccessor classForThisPlatform DefaultDriver: P3DatabaseDriver. - login := Login new - database: PostgreSQLPlatform new; - host: databaseConfiguration pgHostname; - port: databaseConfiguration pgPort; - username: databaseConfiguration pgUsername; - password: databaseConfiguration pgPassword; - databaseName: databaseConfiguration pgDatabaseName; - setSSL; - yourself. - ^ login + | login | + DatabaseAccessor classForThisPlatform DefaultDriver: P3DatabaseDriver. + login := Login new + database: PostgreSQLPlatform new; + host: databaseConfiguration pgHostname; + port: databaseConfiguration pgPort; + username: databaseConfiguration pgUsername; + password: databaseConfiguration pgPassword; + databaseName: databaseConfiguration pgDatabaseName; + setSSL; + yourself. + ^ login ] diff --git a/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st b/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st index a032c43..f325dfe 100644 --- a/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st +++ b/source/Persistent-API-Skeleton-SQLite/SaganParameterDefinitionProvider.extension.st @@ -3,13 +3,13 @@ Extension { #name : 'SaganParameterDefinitionProvider' } { #category : '*Persistent-API-Skeleton-SQLite' } SaganParameterDefinitionProvider class >> saganConfigurationParametersForSQLite [ - ^ OrderedCollection new - add: ( OptionalConfigurationParameter - named: 'Database File Name' - describedBy: 'SQLite Database file name' - inside: self sectionsForSaganConfiguration - defaultingTo: 'default.db' ); - addAll: self saganBasicConfigurationParameters; - addAll: self saganSessionPoolingConfigurationParameters; - yourself + ^ OrderedCollection new + add: ( OptionalConfigurationParameter + named: 'Database File Name' + describedBy: 'SQLite Database file name' + inside: self sectionsForSaganConfiguration + defaultingTo: 'default.db' ); + addAll: self saganBasicConfigurationParameters; + addAll: self saganSessionPoolingConfigurationParameters; + yourself ] diff --git a/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st b/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st index 40ba7bd..4ffbd5c 100644 --- a/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton-SQLite/SingleSQLiteDatabaseProviderModuleFactory.class.st @@ -8,25 +8,25 @@ Class { { #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseBaseName [ - ^self databaseFileName asFileReference asAbsolute basename + ^ self databaseFileName asFileReference asAbsolute basename ] { #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseFileName [ - ^databaseConfiguration databaseFileName + ^ databaseConfiguration databaseFileName ] { #category : 'private' } SingleSQLiteDatabaseProviderModuleFactory >> databaseLogin [ - DatabaseAccessor classForThisPlatform DefaultDriver: SQLite3Driver. - ^ Login new - database: UDBCSQLite3Platform new; - host: self databasePathName; - databaseName: self databaseBaseName; - password: ''; - yourself + DatabaseAccessor classForThisPlatform DefaultDriver: SQLite3Driver. + ^ Login new + database: UDBCSQLite3Platform new; + host: self databasePathName; + databaseName: self databaseBaseName; + password: ''; + yourself ] { #category : 'private' } diff --git a/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st b/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st index a14dcb4..fdb98fd 100644 --- a/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/ConfigureAllowedOriginsTest.class.st @@ -40,13 +40,11 @@ ConfigureAllowedOriginsTest >> testApplyOnNoOriginsAllowed [ { #category : 'tests' } ConfigureAllowedOriginsTest >> testApplyOnOneOriginAllowed [ - ( ConfigureAllowedOrigins - allowing: ( Array with: 'http://allowed-origin-test/' ) - exposing: ( Array with: 'Etag' ) ) applyOn: self. + ( ConfigureAllowedOrigins allowing: #( 'http://allowed-origin-test/' ) exposing: #( 'Etag' ) ) + applyOn: self. - self - assert: allowedOrigins size equals: 1; - assert: allowedOrigins first equals: 'http://allowed-origin-test/'; - assert: exposedHeaders size equals: 1; - assert: exposedHeaders first equals: 'Etag' + self + withTheOnlyOneIn: allowedOrigins + do: [ :allowedOrigin | self assert: allowedOrigin equals: 'http://allowed-origin-test/' ]; + withTheOnlyOneIn: exposedHeaders do: [ :header | self assert: header equals: 'Etag' ] ] diff --git a/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st index 5d34f1b..57b54b6 100644 --- a/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/CreateEmptyRDBMSApplicationTest.class.st @@ -8,13 +8,13 @@ Class { { #category : 'testing' } CreateEmptyRDBMSApplicationTest class >> isAbstract [ - ^ self = CreateEmptyRDBMSApplicationTest + ^ self = CreateEmptyRDBMSApplicationTest ] { #category : 'tests' } CreateEmptyRDBMSApplicationTest >> testCommandName [ - self assert: CreateEmptyRDBMSApplication commandName equals: 'create-empty-rdbms' + self assert: CreateEmptyRDBMSApplication commandName equals: 'create-empty-rdbms' ] { #category : 'tests' } diff --git a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st index d08a8dc..2c195d3 100644 --- a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st +++ b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplication.class.st @@ -5,32 +5,32 @@ Class { #package : 'Persistent-API-Skeleton-Tests' } -{ #category : 'private' } +{ #category : 'accessing' } PersistentPetStoreApplication class >> commandName [ ^ 'persistent-pet-store' ] -{ #category : 'initialization' } +{ #category : 'accessing' } PersistentPetStoreApplication class >> description [ ^ 'A RESTful API for Pet stores' ] -{ #category : 'initialization' } +{ #category : 'class initialization' } PersistentPetStoreApplication class >> initialize [ - - self initializeVersion + + self initializeVersion ] { #category : 'private' } PersistentPetStoreApplication class >> projectName [ - ^ #Stargate + ^ #PersistentAPISkeleton ] -{ #category : 'initialization' } +{ #category : 'accessing' } PersistentPetStoreApplication class >> saganConfigurationParameters [ ^#() @@ -39,29 +39,29 @@ PersistentPetStoreApplication class >> saganConfigurationParameters [ { #category : 'private' } PersistentPetStoreApplication class >> sectionsForStargateConfiguration [ - ^ #( 'Pet Store' ) , super sectionsForStargateConfiguration + ^ #( 'Pet Store' ) , super sectionsForStargateConfiguration ] { #category : 'private - accessing' } PersistentPetStoreApplication >> controllersToInstall [ - ^ { PetsRESTfulController new. PetOrdersRESTfulController new } + ^ { PetsRESTfulController new . PetOrdersRESTfulController new } ] { #category : 'private - accessing' } PersistentPetStoreApplication >> installation [ - ^ PetStoreInstallation installedBy: self + ^ PetStoreInstallation installedBy: self ] { #category : 'private - accessing' } PersistentPetStoreApplication >> serviceDefinitions [ - ^ #() + ^ #( ) ] { #category : 'private - accessing' } PersistentPetStoreApplication >> stargateConfiguration [ - ^self configuration petStore stargate + ^ self configuration petStore stargate ] diff --git a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st index cc13d8c..f7a27f1 100644 --- a/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/PersistentPetStoreApplicationTest.class.st @@ -12,18 +12,21 @@ Class { } { #category : 'configuring' } -PersistentPetStoreApplicationTest >> allowCrossOriginSharingApplying: aFullBlockClosure [ - aFullBlockClosure value: self. +PersistentPetStoreApplicationTest >> allowCrossOriginSharingApplying: aBlock [ + + aBlock value: self ] { #category : 'configuring' } -PersistentPetStoreApplicationTest >> allowOnlyFrom: aCollection [ - allowedOrigins := aCollection +PersistentPetStoreApplicationTest >> allowOnlyFrom: aCollection [ + + allowedOrigins := aCollection ] { #category : 'configuring' } -PersistentPetStoreApplicationTest >> expose: aCollection [ - exposedHeaders := aCollection +PersistentPetStoreApplicationTest >> expose: aCollection [ + + exposedHeaders := aCollection ] { #category : 'running' } @@ -41,138 +44,128 @@ PersistentPetStoreApplicationTest >> setUp [ { #category : 'tests' } PersistentPetStoreApplicationTest >> testAllowedOriginParameter [ - | logRecords | - self start: PersistentPetStoreApplication withAll: { - ('--pet-store.stargate.public-url=http://localhost:<1p>' - expandMacrosWith: port). - ('--pet-store.stargate.port=<1p>' expandMacrosWith: port). - ('--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: - 'secret'). - '--pet-store.stargate.allowed-origins=http://localhost/,http://test:3000/'. - '--pet-store.stargate.consul-agent-location=' }. + | logRecords | + self + start: PersistentPetStoreApplication + withAll: { '--pet-store.stargate.public-url=http://localhost:<1p>' expandMacrosWith: port . + '--pet-store.stargate.port=<1p>' expandMacrosWith: port . + '--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: 'secret' . + '--pet-store.stargate.allowed-origins=http://localhost/,http://test:3000/' . + '--pet-store.stargate.consul-agent-location=' }. - runningApplication configuration parameters - detect: [ :parameter | parameter name = 'Allowed Origins' ] - ifFound: [ :parameter | - self - assert: parameter summary - equals: - 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; - assert: parameter commandLineArgumentName - equals: 'pet-store.stargate.allowed-origins'; - assert: parameter environmentVariableName - equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. - self assert: allowedOrigins isEmpty. - (runningApplication configuration valueFor: parameter) value: self. - self assertCollection: allowedOrigins hasSameElements: { - (WebOrigin basedOn: 'http://localhost/' asUrl). - (WebOrigin basedOn: 'http://test:3000/' asUrl) } ] - ifNone: [ self fail: 'Allowed Origins parameter not found' ]. - logRecords := OrderedCollection new. - logRecords - add: '[INFO] persistent-pet-store'; - add: '[INFO] Obtaining configuration.'; - add: '[WARNING] "Log HTTP Requests" parameter not provided.'; - add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; - add: '[WARNING] "Scheme" parameter not provided. Using default'; - add: - '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; - add: - '[WARNING] "Service Discovery Timeout ms" parameter not provided'; - add: - '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; - add: '[INFO] Public URL:'; - add: '[INFO] Port:'; - add: '[INFO] Operations Secret'; - add: '[INFO] Log HTTP Requests: false'; - add: '[INFO] Concurrent Connections Threshold: 32'; - add: '[INFO] Consul Agent Location'; - add: '[INFO] Scheme: http'; - add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; - add: '[INFO] Service Discovery Timeout ms: 60000'; - add: '[INFO] Service Discovery Time Slot between retries in ms: 100'; - add: - 'Allowed Origins: [ :api | self configureAllowOrigins: webOrigins to: api ]'; - add: '[INFO] Obtaining configuration... [DONE]'; - add: '[INFO] Installing system'; - add: '[INFO] Starting up system'; - add: '[INFO] API Version:'; - add: '[INFO] Creating API...'; - add: '[INFO] Creating API... [DONE]'; - add: '[INFO] Enabling CORS...'; - add: '[INFO] Enabling CORS... [DONE]'; - add: '[INFO] Installing API...'; - add: '[INFO] Installing API... [DONE]'; - add: '[INFO] Starting API...'; - add: '[INFO] Starting API... [DONE]'. - self assertLogRecordsMatch: logRecords + runningApplication configuration parameters + detect: [ :parameter | parameter name = 'Allowed Origins' ] + ifFound: [ :parameter | + self + assert: parameter summary + equals: + 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; + assert: parameter commandLineArgumentName equals: 'pet-store.stargate.allowed-origins'; + assert: parameter environmentVariableName equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. + self assert: allowedOrigins isEmpty. + ( runningApplication configuration valueFor: parameter ) value: self. + self + assertCollection: allowedOrigins + hasSameElements: + { WebOrigin basedOn: 'http://localhost/' asUrl . + WebOrigin basedOn: 'http://test:3000/' asUrl } + ] + ifNone: [ self fail: 'Allowed Origins parameter not found' ]. + logRecords := OrderedCollection new. + logRecords + add: '[INFO] persistent-pet-store'; + add: '[INFO] Obtaining configuration.'; + add: '[WARNING] "Log HTTP Requests" parameter not provided.'; + add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; + add: '[WARNING] "Scheme" parameter not provided. Using default'; + add: '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; + add: '[WARNING] "Service Discovery Timeout ms" parameter not provided'; + add: '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; + add: '[INFO] Public URL:'; + add: '[INFO] Port:'; + add: '[INFO] Operations Secret'; + add: '[INFO] Log HTTP Requests: false'; + add: '[INFO] Concurrent Connections Threshold: 32'; + add: '[INFO] Consul Agent Location'; + add: '[INFO] Scheme: http'; + add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; + add: '[INFO] Service Discovery Timeout ms: 60000'; + add: '[INFO] Service Discovery Time Slot between retries in ms: 100'; + add: 'Allowed Origins: [ :api | self configureAllowOrigins: webOrigins to: api ]'; + add: '[INFO] Obtaining configuration... [DONE]'; + add: '[INFO] Installing system'; + add: '[INFO] Starting up system'; + add: '[INFO] API Version:'; + add: '[INFO] Creating API...'; + add: '[INFO] Creating API... [DONE]'; + add: '[INFO] Enabling CORS...'; + add: '[INFO] Enabling CORS... [DONE]'; + add: '[INFO] Installing API...'; + add: '[INFO] Installing API... [DONE]'; + add: '[INFO] Starting API...'; + add: '[INFO] Starting API... [DONE]'. + self assertLogRecordsMatch: logRecords ] { #category : 'tests' } PersistentPetStoreApplicationTest >> testMissingAllowedOriginParameter [ - | logRecords | - self start: PersistentPetStoreApplication withAll: { - ('--pet-store.stargate.public-url=http://localhost:<1p>' - expandMacrosWith: port). - ('--pet-store.stargate.port=<1p>' expandMacrosWith: port). - ('--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: - 'secret'). - '--pet-store.stargate.consul-agent-location=' }. + | logRecords | + self + start: PersistentPetStoreApplication + withAll: { '--pet-store.stargate.public-url=http://localhost:<1p>' expandMacrosWith: port . + '--pet-store.stargate.port=<1p>' expandMacrosWith: port . + '--pet-store.stargate.operations-secret=<1s>' expandMacrosWith: 'secret' . + '--pet-store.stargate.consul-agent-location=' }. - runningApplication configuration parameters - detect: [ :parameter | parameter name = 'Allowed Origins' ] - ifFound: [ :parameter | - self - assert: parameter summary - equals: - 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; - assert: parameter commandLineArgumentName - equals: 'pet-store.stargate.allowed-origins'; - assert: parameter environmentVariableName - equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. - self assert: allowedOrigins isEmpty. - self assert: exposedHeaders isEmpty. - (runningApplication configuration valueFor: parameter) value: self. - self assert: allowedOrigins isEmpty. - self assert: exposedHeaders isEmpty. - logRecords := OrderedCollection new. - logRecords - add: '[INFO] persistent-pet-store'; - add: '[INFO] Obtaining configuration.'; - add: '[WARNING] "Log HTTP Requests" parameter not provided.'; - add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; - add: '[WARNING] "Scheme" parameter not provided. Using default'; - add: - '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; - add: - '[WARNING] "Service Discovery Timeout ms" parameter not provided'; - add: - '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; - add: - '[WARNING] "Allowed Origins" parameter not provided. Using default.'; - add: '[INFO] Public URL:'; - add: '[INFO] Port:'; - add: '[INFO] Operations Secret'; - add: '[INFO] Log HTTP Requests: false'; - add: '[INFO] Concurrent Connections Threshold: 32'; - add: '[INFO] Consul Agent Location'; - add: '[INFO] Scheme: http'; - add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; - add: '[INFO] Service Discovery Timeout ms: 60000'; - add: - '[INFO] Service Discovery Time Slot between retries in ms: 100'; - add: '[INFO] Allowed Origins: [ :api | ]'; - add: '[INFO] Obtaining configuration... [DONE]'; - add: '[INFO] Installing system'; - add: '[INFO] Starting up system'; - add: '[INFO] API Version: '; - add: '[INFO] Creating API...'; - add: '[INFO] Creating API... [DONE]'; - add: '[INFO] Installing API...'; - add: '[INFO] Installing API... [DONE]'; - add: '[INFO] Starting API...'; - add: '[INFO] Starting API... [DONE]'. - self assertLogRecordsMatch: logRecords ] - ifNone: [ self fail: 'Allowed Origins parameter not found' ] + runningApplication configuration parameters + detect: [ :parameter | parameter name = 'Allowed Origins' ] + ifFound: [ :parameter | + self + assert: parameter summary + equals: + 'A comma-separated list of URLs used as allowed origins for CORS. Defaults to [ :api | ]'; + assert: parameter commandLineArgumentName equals: 'pet-store.stargate.allowed-origins'; + assert: parameter environmentVariableName equals: 'PET_STORE__STARGATE__ALLOWED_ORIGINS'. + self assert: allowedOrigins isEmpty. + self assert: exposedHeaders isEmpty. + ( runningApplication configuration valueFor: parameter ) value: self. + self assert: allowedOrigins isEmpty. + self assert: exposedHeaders isEmpty. + logRecords := OrderedCollection new. + logRecords + add: '[INFO] persistent-pet-store'; + add: '[INFO] Obtaining configuration.'; + add: '[WARNING] "Log HTTP Requests" parameter not provided.'; + add: '[WARNING] "Concurrent Connections Threshold" parameter not provided.'; + add: '[WARNING] "Scheme" parameter not provided. Using default'; + add: '[WARNING] "Service Discovery Healthcheck interval ms" parameter not provided'; + add: '[WARNING] "Service Discovery Timeout ms" parameter not provided'; + add: + '[WARNING] "Service Discovery Time Slot between retries in ms" parameter not provided'; + add: '[WARNING] "Allowed Origins" parameter not provided. Using default.'; + add: '[INFO] Public URL:'; + add: '[INFO] Port:'; + add: '[INFO] Operations Secret'; + add: '[INFO] Log HTTP Requests: false'; + add: '[INFO] Concurrent Connections Threshold: 32'; + add: '[INFO] Consul Agent Location'; + add: '[INFO] Scheme: http'; + add: '[INFO] Service Discovery Healthcheck interval ms: 10000'; + add: '[INFO] Service Discovery Timeout ms: 60000'; + add: '[INFO] Service Discovery Time Slot between retries in ms: 100'; + add: '[INFO] Allowed Origins: [ :api | ]'; + add: '[INFO] Obtaining configuration... [DONE]'; + add: '[INFO] Installing system'; + add: '[INFO] Starting up system'; + add: '[INFO] API Version: '; + add: '[INFO] Creating API...'; + add: '[INFO] Creating API... [DONE]'; + add: '[INFO] Installing API...'; + add: '[INFO] Installing API... [DONE]'; + add: '[INFO] Starting API...'; + add: '[INFO] Starting API... [DONE]'. + self assertLogRecordsMatch: logRecords + ] + ifNone: [ self fail: 'Allowed Origins parameter not found' ] ] diff --git a/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st b/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st index 3006f8f..62aa12a 100644 --- a/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st +++ b/source/Persistent-API-Skeleton-Tests/PetStoreInstallation.class.st @@ -1,9 +1,6 @@ Class { #name : 'PetStoreInstallation', #superclass : 'SystemInstallation', - #instVars : [ - 'application' - ], #category : 'Persistent-API-Skeleton-Tests', #package : 'Persistent-API-Skeleton-Tests' } @@ -11,27 +8,23 @@ Class { { #category : 'instance creation' } PetStoreInstallation class >> installedBy: anApplication [ -^self new initializeInstalledBy: anApplication + ^ self new ] { #category : 'installing' } -PetStoreInstallation >> beAwareOfShutDownOf: aCompositeSystem [ -] - -{ #category : 'installing' } -PetStoreInstallation >> initializeInstalledBy: anApplication [ +PetStoreInstallation >> beAwareOfShutDownOf: aCompositeSystem [ -application := anApplication + ] -{ #category : 'installing' } +{ #category : 'accessing' } PetStoreInstallation >> modulesToInstall [ ^#() ] -{ #category : 'installing' } +{ #category : 'accessing' } PetStoreInstallation >> name [ - ^'Pet Store' + ^ 'Pet Store' ] diff --git a/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st b/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st index 7bfaa88..3bc35f8 100644 --- a/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/RDBMSMappingConfigurationTest.class.st @@ -11,51 +11,47 @@ Class { { #category : 'tests' } RDBMSMappingConfigurationTest >> testRemoteReferenceMapping [ - | definition | - - definition := RDBMSMappingConfiguration new remoteReferenceConversionDefinition. - - DatabasePlatform - allSubclassesDo: [ :platformClass | - | converter field | - - field := DatabaseField named: 'self_location' type: ( platformClass new varChar: 256 ). - converter := ( definition - defineFor: ( DirectMapping from: #selfLocation to: field ) - using: ConfigurableDescriptorSystem new ) converter. - - self - assert: - ( converter - convert: ( RemoteReference locatedAt: 'http://api.example.com' asUrl ) - toDatabaseRepresentationAs: field type ) - equals: 'http://api.example.com/'; - assert: ( converter convert: 'http://api.example.com' fromDatabaseRepresentationAs: field type ) - equals: ( RemoteReference locatedAt: 'http://api.example.com' asUrl ) - ] + | definition | + definition := RDBMSMappingConfiguration new remoteReferenceConversionDefinition. + + DatabasePlatform allSubclassesDo: [ :platformClass | + | converter field | + field := DatabaseField named: 'self_location' type: ( platformClass new varChar: 256 ). + converter := ( definition + defineFor: ( DirectMapping from: #selfLocation to: field ) + using: ConfigurableDescriptorSystem new ) converter. + + self + assert: ( converter + convert: ( RemoteReference locatedAt: 'http://api.example.com' asUrl ) + toDatabaseRepresentationAs: field type ) + equals: 'http://api.example.com/'; + assert: + ( converter convert: 'http://api.example.com' fromDatabaseRepresentationAs: field type ) + equals: ( RemoteReference locatedAt: 'http://api.example.com' asUrl ) + ] ] { #category : 'tests' } RDBMSMappingConfigurationTest >> testUUIDMapping [ - | definition | - - definition := RDBMSMappingConfiguration new uuidConversionDefinition. - - DatabasePlatform - allSubclassesDo: [ :platformClass | - | converter field | - - field := DatabaseField named: 'uuid' type: ( platformClass new varChar: 36 ). - converter := ( definition - defineFor: ( DirectMapping from: #uuid to: field ) - using: ConfigurableDescriptorSystem new ) converter. - - self - assert: ( converter convert: UUID nilUUID toDatabaseRepresentationAs: field type ) - equals: '00000000-0000-0000-0000-000000000000'; - assert: - ( converter convert: '00000000-0000-0000-0000-000000000000' fromDatabaseRepresentationAs: field type ) - equals: UUID nilUUID - ] + | definition | + definition := RDBMSMappingConfiguration new uuidConversionDefinition. + + DatabasePlatform allSubclassesDo: [ :platformClass | + | converter field | + field := DatabaseField named: 'uuid' type: ( platformClass new varChar: 36 ). + converter := ( definition + defineFor: ( DirectMapping from: #uuid to: field ) + using: ConfigurableDescriptorSystem new ) converter. + + self + assert: ( converter convert: UUID nilUUID toDatabaseRepresentationAs: field type ) + equals: '00000000-0000-0000-0000-000000000000'; + assert: + ( converter + convert: '00000000-0000-0000-0000-000000000000' + fromDatabaseRepresentationAs: field type ) + equals: UUID nilUUID + ] ] diff --git a/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st b/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st index a5bd987..4d8da35 100644 --- a/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/SinglePostgreSQLDatabaseProviderModuleFactoryTest.class.st @@ -8,23 +8,21 @@ Class { { #category : 'tests' } SinglePostgreSQLDatabaseProviderModuleFactoryTest >> testDatabaseLogin [ - | login configuration | + | login configuration | + configuration := ApplicationConfiguration + forAll: CreateEmptyRDBMSApplication saganConfigurationParametersForPostgreSQL + providedBy: ( self commandLineProviderOver: + #( '--sagan.pg-hostname=localhost' '--sagan.pg-username=postgres' + '--sagan.pg-password=secret' '--sagan.pg-database-name=test' ) ). - configuration := ApplicationConfiguration - forAll: - CreateEmptyRDBMSApplication saganConfigurationParametersForPostgreSQL - providedBy: ( self commandLineProviderOver: - #( '--sagan.pg-hostname=localhost' '--sagan.pg-username=postgres' - '--sagan.pg-password=secret' '--sagan.pg-database-name=test' ) ). + login := ( SinglePostgreSQLDatabaseProviderModuleFactory configuredBy: configuration sagan ) + databaseLogin. - login := ( SinglePostgreSQLDatabaseProviderModuleFactory configuredBy: configuration sagan ) - databaseLogin. - - self - assert: login platform isPostgreSQLPlatform; - assert: login host equals: 'localhost'; - assert: login port equals: 5432; - assert: login username equals: 'postgres'; - assert: login password equals: 'secret'; - assert: login databaseName equals: 'test' + self + assert: login platform isPostgreSQLPlatform; + assert: login host equals: 'localhost'; + assert: login port equals: 5432; + assert: login username equals: 'postgres'; + assert: login password equals: 'secret'; + assert: login databaseName equals: 'test' ] diff --git a/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st b/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st index 0138a73..52b1c73 100644 --- a/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st +++ b/source/Persistent-API-Skeleton-Tests/SingleSQLiteDatabaseProviderModuleFactoryTest.class.st @@ -11,36 +11,34 @@ Class { { #category : 'tests' } SingleSQLiteDatabaseProviderModuleFactoryTest >> testDatabaseLogin [ - | factory configuration | - - configuration := ApplicationConfiguration - forAll: CreateEmptyRDBMSApplication saganConfigurationParametersForSQLite - providedBy: - ( self commandLineProviderOver: #( '--sagan.database-file-name=/tmp/tmp.db' ) ). - - factory := SingleSQLiteDatabaseProviderModuleFactory configuredBy: configuration sagan. - - self - assert: factory databaseLogin platform isSQLite3Platform; - assert: factory databaseLogin databaseName equals: 'tmp.db'; - assert: factory databaseLogin host equals: '/tmp/'; - assert: factory databaseLogin password isEmpty + | factory configuration | + configuration := ApplicationConfiguration + forAll: CreateEmptyRDBMSApplication saganConfigurationParametersForSQLite + providedBy: + ( self commandLineProviderOver: #( '--sagan.database-file-name=/tmp/tmp.db' ) ). + + factory := SingleSQLiteDatabaseProviderModuleFactory configuredBy: configuration sagan. + + self + assert: factory databaseLogin platform isSQLite3Platform; + assert: factory databaseLogin databaseName equals: 'tmp.db'; + assert: factory databaseLogin host equals: '/tmp/'; + assert: factory databaseLogin password isEmpty ] { #category : 'tests' } SingleSQLiteDatabaseProviderModuleFactoryTest >> testDefaultDatabaseLogin [ - | factory configuration | - - configuration := ApplicationConfiguration - forAll: CreateEmptyRDBMSApplication saganConfigurationParametersForSQLite - providedBy: NullConfigurationProvider new. + | factory configuration | + configuration := ApplicationConfiguration + forAll: CreateEmptyRDBMSApplication saganConfigurationParametersForSQLite + providedBy: NullConfigurationProvider new. - factory := SingleSQLiteDatabaseProviderModuleFactory configuredBy: configuration sagan. + factory := SingleSQLiteDatabaseProviderModuleFactory configuredBy: configuration sagan. - self - assert: factory databaseLogin platform isSQLite3Platform; - assert: factory databaseLogin databaseName equals: 'default.db'; - assert: factory databaseLogin host notEmpty; - assert: factory databaseLogin password isEmpty + self + assert: factory databaseLogin platform isSQLite3Platform; + assert: factory databaseLogin databaseName equals: 'default.db'; + assert: factory databaseLogin host notEmpty; + assert: factory databaseLogin password isEmpty ] diff --git a/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st b/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st index 1c7c174..a1ab30b 100644 --- a/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st +++ b/source/Persistent-API-Skeleton/CompositeStackTraceDumper.class.st @@ -11,17 +11,17 @@ Class { { #category : 'instance creation' } CompositeStackTraceDumper class >> composingAll: dumpers [ - ^ self new initializeComposingAll: dumpers + ^ self new initializeComposingAll: dumpers ] -{ #category : 'initialization' } +{ #category : 'error handling' } CompositeStackTraceDumper >> dumpStackTraceFor: anError [ - dumpers do: [ :dumper | dumper dumpStackTraceFor: anError ] + dumpers do: [ :dumper | dumper dumpStackTraceFor: anError ] ] { #category : 'initialization' } CompositeStackTraceDumper >> initializeComposingAll: aDumpersCollection [ - dumpers := aDumpersCollection + dumpers := aDumpersCollection ] diff --git a/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st b/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st index fb5e351..7a6e96f 100644 --- a/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st +++ b/source/Persistent-API-Skeleton/CreateEmptyRDBMSApplication.class.st @@ -11,19 +11,19 @@ Class { { #category : 'accessing' } CreateEmptyRDBMSApplication class >> commandName [ - ^ 'create-empty-rdbms' + ^ 'create-empty-rdbms' ] { #category : 'accessing' } CreateEmptyRDBMSApplication class >> configurationParameters [ - ^ self saganConfigurationParameters + ^ self saganConfigurationParameters ] { #category : 'accessing' } CreateEmptyRDBMSApplication class >> description [ - ^ 'Creates the database structure' + ^ 'Creates the database structure' ] { #category : 'private' } @@ -55,52 +55,51 @@ CreateEmptyRDBMSApplication class >> logsDirectory [ { #category : 'accessing' } CreateEmptyRDBMSApplication class >> projectName [ - self subclassResponsibility + self subclassResponsibility ] { #category : 'accessing' } CreateEmptyRDBMSApplication class >> saganConfigurationParameters [ - self subclassResponsibility + self subclassResponsibility ] -{ #category : 'versions' } +{ #category : 'accessing' } CreateEmptyRDBMSApplication class >> version [ - ^ Version + ^ Version ] -{ #category : 'private - activation' } +{ #category : 'private - activation/deactivation' } CreateEmptyRDBMSApplication >> basicStartWithin: context [ - | rootSystem | - - LogRecord emitInfo: 'Installing system'. - rootSystem := self installation install: self class version. - LogRecord emitInfo: 'Starting up system'. - rootSystem startUp. - LogRecord emitInfo: 'Setting up database structure'. - ( rootSystem >> #RepositoryProviderSystem ) prepareForInitialPersistence. - LogRecord emitInfo: 'Shutting down system'. - rootSystem shutDown. - self exitSuccess + | rootSystem | + LogRecord emitInfo: 'Installing system'. + rootSystem := self installation install: self class version. + LogRecord emitInfo: 'Starting up system'. + rootSystem startUp. + LogRecord emitInfo: 'Setting up database structure'. + ( rootSystem >> #RepositoryProviderSystem ) prepareForInitialPersistence. + LogRecord emitInfo: 'Shutting down system'. + rootSystem shutDown. + self exitSuccess ] { #category : 'private - accessing' } CreateEmptyRDBMSApplication >> installation [ - ^ self subclassResponsibility + ^ self subclassResponsibility ] { #category : 'private - accessing' } CreateEmptyRDBMSApplication >> saganConfiguration [ - ^ self configuration sagan + ^ self configuration sagan ] { #category : 'error handling' } CreateEmptyRDBMSApplication >> stackTraceDumper [ - ^ StackTraceBinarySerializer on: [ :dumpAction | - self class fileReferenceToDumpStackTrace binaryWriteStreamDo: dumpAction ] + ^ StackTraceBinarySerializer on: [ :dumpAction | + self class fileReferenceToDumpStackTrace binaryWriteStreamDo: dumpAction ] ] diff --git a/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st b/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st index 4fdbb80..14f8242 100644 --- a/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st +++ b/source/Persistent-API-Skeleton/PersistentAPIApplication.class.st @@ -126,48 +126,48 @@ PersistentAPIApplication class >> stargateConfigurationParameters [ { #category : 'private - activation/deactivation' } PersistentAPIApplication >> basicStartWithin: context [ - self installAndStartRootSystem. - self handleOptionsIn: context. - super basicStartWithin: context + self installAndStartRootSystem. + self handleOptionsIn: context. + super basicStartWithin: context ] { #category : 'private - activation/deactivation' } -PersistentAPIApplication >> basicStop [ +PersistentAPIApplication >> basicStop [ - super basicStop. - LogRecord emitInfo: 'Stopping system'. - rootSystem ifNotNil: #shutDown + super basicStop. + LogRecord emitInfo: 'Stopping system'. + rootSystem ifNotNil: #shutDown ] { #category : 'private - activation/deactivation' } PersistentAPIApplication >> configureAllowedOriginsTo: api [ - self stargateConfiguration allowedOrigins value: api. - + self stargateConfiguration allowedOrigins value: api ] { #category : 'private - activation/deactivation' } PersistentAPIApplication >> createAPI [ - | api | - api := super createAPI. - self configureAllowedOriginsTo: api. - ^ api + | api | + api := super createAPI. + self configureAllowedOriginsTo: api. + ^ api ] { #category : 'private - activation/deactivation' } PersistentAPIApplication >> handleOptionsIn: context [ + "I'm a template method. Subclasses can re-implement me to handle options." - "I'm a template method. Subclasses can re-implement me to handle options." + ] { #category : 'private - activation/deactivation' } PersistentAPIApplication >> installAndStartRootSystem [ - LogRecord emitInfo: 'Installing system'. - rootSystem := self installation install: self class version. - LogRecord emitInfo: 'Starting up system'. - rootSystem startUp + LogRecord emitInfo: 'Installing system'. + rootSystem := self installation install: self class version. + LogRecord emitInfo: 'Starting up system'. + rootSystem startUp ] { #category : 'private - accessing' } @@ -182,37 +182,38 @@ PersistentAPIApplication >> saganConfiguration [ ^ self configuration sagan ] -{ #category : 'private - building' } +{ #category : 'private - accessing' } PersistentAPIApplication >> serviceDiscoveryHealthCheckInterval [ - ^ self stargateConfiguration serviceDiscoveryHealthcheckIntervalMs milliSeconds + ^ self stargateConfiguration serviceDiscoveryHealthcheckIntervalMs milliSeconds ] -{ #category : 'private - building' } +{ #category : 'private - accessing' } PersistentAPIApplication >> serviceDiscoveryHealthCheckTimeout [ ^ self stargateConfiguration serviceDiscoveryTimeoutMs milliSeconds ] -{ #category : 'private - building' } +{ #category : 'private - accessing' } PersistentAPIApplication >> serviceDiscoveryTimeSlotBetweenRetries [ ^ self stargateConfiguration serviceDiscoveryTimeSlotBetweenRetriesInMs milliSeconds ] -{ #category : 'private - building' } +{ #category : 'error handling' } PersistentAPIApplication >> stackTraceDumpFileReference [ - ^ self class logsDirectory / ( '<1s>-<2s>' expandMacrosWith: self class commandName - with: ( ( ZTimestampFormat fromString: '2001-02-03_16-05-06.07' ) format: ZTimestamp now ) ) + ^ self class logsDirectory / ( '<1s>-<2s>' + expandMacrosWith: self class commandName + with: ( ( ZTimestampFormat fromString: '2001-02-03_16-05-06.07' ) format: ZTimestamp now ) ) ] -{ #category : 'private - building' } +{ #category : 'error handling' } PersistentAPIApplication >> stackTraceDumper [ - ^ CompositeStackTraceDumper composingAll: { - ( StackTraceTextDumper on: [ :dumpAction | - ( self stackTraceDumpFileReference withExtension: 'log' ) writeStreamDo: dumpAction ] ). - ( StackTraceBinarySerializer on: [ :dumpAction | - ( self stackTraceDumpFileReference withExtension: 'fuel' ) binaryWriteStreamDo: dumpAction ] ) } + ^ CompositeStackTraceDumper composingAll: { + StackTraceTextDumper on: [ :dumpAction | + ( self stackTraceDumpFileReference withExtension: 'log' ) writeStreamDo: dumpAction ] . + StackTraceBinarySerializer on: [ :dumpAction | + ( self stackTraceDumpFileReference withExtension: 'fuel' ) binaryWriteStreamDo: dumpAction ] } ] diff --git a/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st b/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st index 06742d0..192ce97 100644 --- a/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st +++ b/source/Persistent-API-Skeleton/RDBMSMappingConfiguration.extension.st @@ -3,35 +3,39 @@ Extension { #name : 'RDBMSMappingConfiguration' } { #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> mappingForOptional: anAttributeName to: aFieldName on: aTableName [ - | dbToStConversion stToDbConversion | - - dbToStConversion := [ :valueOrNil | - valueOrNil ifNil: [ Optional unused ] - ifNotNil: [ :value | Optional containing: value ] ]. - stToDbConversion := [ :optional | optional withContentDo: [ :value | value ] ifUnused: [ nil ] ]. - - ^ AdHocMappingDefinition forAttributeNamed: anAttributeName - sending: #value: - to: [ :value | value ] - toMapAssociations: { ( AdHocMappingFieldAndValueAssociation relating: #yourself - to: aFieldName - on: aTableName - convertingFromDatabaseToSmalltalkUsing: dbToStConversion - fromSmalltalkToDatabaseUsing: stToDbConversion ) } + | dbToStConversion stToDbConversion | + dbToStConversion := [ :valueOrNil | + valueOrNil + ifNil: [ Optional unused ] + ifNotNil: [ :value | Optional containing: value ] ]. + stToDbConversion := [ :optional | optional withContentDo: [ :value | value ] ifUnused: [ nil ] ]. + + ^ AdHocMappingDefinition + forAttributeNamed: anAttributeName + sending: #value: + to: [ :value | value ] + toMapAssociations: { AdHocMappingFieldAndValueAssociation + relating: #yourself + to: aFieldName + on: aTableName + convertingFromDatabaseToSmalltalkUsing: dbToStConversion + fromSmalltalkToDatabaseUsing: stToDbConversion } ] { #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> remoteReferenceConversionDefinition [ - ^ PluggableMappingConversionDefinition named: 'Remote Reference' - convertingFromDatabaseToSmalltalkUsing: [ :string | RemoteReference locatedAt: string asUrl ] - fromSmalltalkToDatabaseUsing: [ :reference | reference selfLocation asString ] + ^ PluggableMappingConversionDefinition + named: 'Remote Reference' + convertingFromDatabaseToSmalltalkUsing: [ :string | RemoteReference locatedAt: string asUrl ] + fromSmalltalkToDatabaseUsing: [ :reference | reference selfLocation asString ] ] { #category : '*Persistent-API-Skeleton' } RDBMSMappingConfiguration >> uuidConversionDefinition [ - ^ PluggableMappingConversionDefinition named: 'UUID' - convertingFromDatabaseToSmalltalkUsing: [ :string | UUID fromString: string ] - fromSmalltalkToDatabaseUsing: [ :uuid | uuid printString ] + ^ PluggableMappingConversionDefinition + named: 'UUID' + convertingFromDatabaseToSmalltalkUsing: [ :string | UUID fromString: string ] + fromSmalltalkToDatabaseUsing: [ :uuid | uuid printString ] ] diff --git a/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st b/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st index 41df1b0..6cb1c2f 100644 --- a/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st +++ b/source/Persistent-API-Skeleton/SaganParameterDefinitionProvider.class.st @@ -38,51 +38,49 @@ SaganParameterDefinitionProvider class >> defaultTimeSlotBetweenConnectionRetrie { #category : 'private' } SaganParameterDefinitionProvider class >> saganBasicConfigurationParameters [ - ^ OrderedCollection new - add: ( OptionalConfigurationParameter - named: 'Log Database Events' - describedBy: 'A boolean indicating whether should log database events or not' - inside: self sectionsForSaganConfiguration - defaultingTo: false - convertingWith: #asBoolean ); - add: ( OptionalConfigurationParameter - named: 'Maximum Connection Attemps' - describedBy: 'Number of retries login database attempts' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMaxConnectionAttemps - convertingWith: #asNumber ); - add: ( OptionalConfigurationParameter - named: 'Time Slot Between Connection Retries In ms' - describedBy: 'Timeframe in milliseconds to wait before retrying a login to database' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultTimeSlotBetweenConnectionRetries - convertingWith: [ :parameter | Duration milliSeconds: parameter asNumber ] ); - yourself + ^ OrderedCollection + with: ( OptionalConfigurationParameter + named: 'Log Database Events' + describedBy: 'A boolean indicating whether should log database events or not' + inside: self sectionsForSaganConfiguration + defaultingTo: false + convertingWith: #asBoolean ) + with: ( OptionalConfigurationParameter + named: 'Maximum Connection Attemps' + describedBy: 'Number of retries login database attempts' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxConnectionAttemps + convertingWith: #asNumber ) + with: ( OptionalConfigurationParameter + named: 'Time Slot Between Connection Retries In ms' + describedBy: 'Timeframe in milliseconds to wait before retrying a login to database' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultTimeSlotBetweenConnectionRetries + convertingWith: [ :parameter | Duration milliSeconds: parameter asNumber ] ) ] { #category : 'private' } SaganParameterDefinitionProvider class >> saganSessionPoolingConfigurationParameters [ - ^ OrderedCollection new - add: ( OptionalConfigurationParameter - named: 'Min Idle Sessions Count' - describedBy: 'Minimum number of idle sessions to keep alive in the pool' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMinIdleSessionsCount - convertingWith: #asNumber ); - add: ( OptionalConfigurationParameter - named: 'Max Idle Sessions Count' - describedBy: 'Maximum number of idle sessions to keep alive in the pool' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMaxIdleSessionsCount - convertingWith: #asNumber ); - add: ( OptionalConfigurationParameter - named: 'Max Active Sessions Count' - describedBy: 'Maximum number of sessions alive' - inside: self sectionsForSaganConfiguration - defaultingTo: self defaultMaxActiveSessionsCount - convertingWith: #asNumber ); - yourself + ^ OrderedCollection + with: ( OptionalConfigurationParameter + named: 'Min Idle Sessions Count' + describedBy: 'Minimum number of idle sessions to keep alive in the pool' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMinIdleSessionsCount + convertingWith: #asNumber ) + with: ( OptionalConfigurationParameter + named: 'Max Idle Sessions Count' + describedBy: 'Maximum number of idle sessions to keep alive in the pool' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxIdleSessionsCount + convertingWith: #asNumber ) + with: ( OptionalConfigurationParameter + named: 'Max Active Sessions Count' + describedBy: 'Maximum number of sessions alive' + inside: self sectionsForSaganConfiguration + defaultingTo: self defaultMaxActiveSessionsCount + convertingWith: #asNumber ) ] { #category : 'private' } diff --git a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st index 3048af8..bd55195 100644 --- a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st +++ b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModule.class.st @@ -13,41 +13,43 @@ Class { { #category : 'instance creation' } SingleDatabaseRepositoryProviderModule class >> toInstallOn: aCompositeSystem usingSessionPoolWith: aDatabaseLogin configuredBy: aConfigurationAction [ - ^ self new - initializeToInstallOn: aCompositeSystem - usingSessionPoolWith: aDatabaseLogin - configuredBy: aConfigurationAction + ^ self new + initializeToInstallOn: aCompositeSystem + usingSessionPoolWith: aDatabaseLogin + configuredBy: aConfigurationAction ] { #category : 'initialization' } SingleDatabaseRepositoryProviderModule >> initializeToInstallOn: aCompositeSystem usingSessionPoolWith: aDatabaseLogin configuredBy: aConfigurationAction [ - rootSystem := aCompositeSystem. - login := aDatabaseLogin. - configurationAction := aConfigurationAction + rootSystem := aCompositeSystem. + login := aDatabaseLogin. + configurationAction := aConfigurationAction ] { #category : 'private' } SingleDatabaseRepositoryProviderModule >> name [ - ^'Single Database Repository Provider' + ^ 'Single Database Repository Provider' ] { #category : 'private' } SingleDatabaseRepositoryProviderModule >> registerRepositoryProviderSystemForInstallationIn: systems [ - ^ self - register: [ - RepositoryProviderSystem new - register: - ( RDBMSRepositoryProvider usingSessionPoolWith: login configuredBy: configurationAction ) - as: #mainDB; - yourself - ] - in: systems + ^ self + register: [ + RepositoryProviderSystem new + register: + ( RDBMSRepositoryProvider + usingSessionPoolWith: login + configuredBy: configurationAction ) + as: #mainDB; + yourself + ] + in: systems ] -{ #category : 'accessing' } +{ #category : 'private' } SingleDatabaseRepositoryProviderModule >> rootSystem [ ^ rootSystem @@ -56,5 +58,5 @@ SingleDatabaseRepositoryProviderModule >> rootSystem [ { #category : 'private' } SingleDatabaseRepositoryProviderModule >> systemInterfacesToInstall [ - ^#(RepositoryProviderSystem) + ^ #( RepositoryProviderSystem ) ] diff --git a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st index 06dc6d5..495fbd4 100644 --- a/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st +++ b/source/Persistent-API-Skeleton/SingleDatabaseRepositoryProviderModuleFactory.class.st @@ -26,21 +26,21 @@ SingleDatabaseRepositoryProviderModuleFactory class >> configuredBy: theDatabase ] { #category : 'testing' } -SingleDatabaseRepositoryProviderModuleFactory class >> isAbstract [ +SingleDatabaseRepositoryProviderModuleFactory class >> isAbstract [ - ^self = SingleDatabaseRepositoryProviderModuleFactory + ^ self = SingleDatabaseRepositoryProviderModuleFactory ] { #category : 'private' } SingleDatabaseRepositoryProviderModuleFactory >> databaseLogin [ - self subclassResponsibility + self subclassResponsibility ] { #category : 'initialization' } SingleDatabaseRepositoryProviderModuleFactory >> initializeConfiguredBy: theDatabaseConfiguration [ - databaseConfiguration := theDatabaseConfiguration. + databaseConfiguration := theDatabaseConfiguration ] { #category : 'instance creation' } From 40201ad2fdfd30a909458843ebe273ba24a6fc56 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Tue, 15 Jul 2025 11:33:21 -0300 Subject: [PATCH 4/5] Remove extensions already provided by Stargate --- .../API-Skeleton/NeoJSONReader.extension.st | 19 ------- .../NeoJSONStrictObjectMapping.class.st | 56 ------------------- 2 files changed, 75 deletions(-) delete mode 100644 source/API-Skeleton/NeoJSONStrictObjectMapping.class.st diff --git a/source/API-Skeleton/NeoJSONReader.extension.st b/source/API-Skeleton/NeoJSONReader.extension.st index 83505a7..46021d5 100644 --- a/source/API-Skeleton/NeoJSONReader.extension.st +++ b/source/API-Skeleton/NeoJSONReader.extension.st @@ -1,14 +1,5 @@ Extension { #name : 'NeoJSONReader' } -{ #category : '*API-Skeleton' } -NeoJSONReader >> for: schemaName strictDo: block [ - - | mapping | - mapping := self strictMappingFor: schemaName. - block value: mapping. - ^ mapping -] - { #category : '*API-Skeleton' } NeoJSONReader >> mapRemoteReference [ @@ -32,13 +23,3 @@ NeoJSONReader >> mapRemoteReference [ ] ] ] - -{ #category : '*API-Skeleton' } -NeoJSONReader >> strictMappingFor: smalltalkClass [ - - ^ self mappings at: smalltalkClass ifAbsentPut: [ - NeoJSONStrictObjectMapping new - subjectClass: smalltalkClass; - yourself - ] -] diff --git a/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st b/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st deleted file mode 100644 index 8201f96..0000000 --- a/source/API-Skeleton/NeoJSONStrictObjectMapping.class.st +++ /dev/null @@ -1,56 +0,0 @@ -" -I am NeoJSONStrictObjectMapping, I'm equivalent to NeoJSONObjectMapping but more strict. -I will fail on reading properties of an object if some of the mapped properties are missing in the incoming JSON. -" -Class { - #name : 'NeoJSONStrictObjectMapping', - #superclass : 'NeoJSONObjectMapping', - #category : 'API-Skeleton', - #package : 'API-Skeleton' -} - -{ #category : 'private' } -NeoJSONStrictObjectMapping >> errorDescriptionForMissing: propertyNames [ - - ^ String streamContents: [ :stream | - stream - nextPutAll: 'Missing required keys'; - space; - nextPut: $(. - propertyNames - do: [ :propertyName | - stream - nextPut: $#; - nextPutAll: propertyName - ] - separatedBy: [ - stream - nextPut: $,; - space - ]. - stream nextPut: $) - ] -] - -{ #category : 'parsing' } -NeoJSONStrictObjectMapping >> readFrom: jsonReader [ - - | anObject propertyNames | - anObject := subjectClass new. - allowNil ifTrue: [ - jsonReader parseConstantDo: [ :value | - ^ value ifNotNil: [ jsonReader error: 'Unexpected boolean constant' ] ] - ]. - propertyNames := properties collect: [ :each | each propertyName ]. - jsonReader parseMapKeysDo: [ :key | - ( self propertyNamed: key ifAbsent: [ nil ] ) - ifNil: [ "read, skip & ignore value" jsonReader next ] - ifNotNil: [ :mapping | - propertyNames remove: key asSymbol. - mapping readObject: anObject from: jsonReader - ] - ]. - propertyNames ifNotEmpty: [ jsonReader error: ( self errorDescriptionForMissing: propertyNames ) ]. - ( anObject respondsTo: #assertIsValid ) then: [ anObject assertIsValid ]. - ^ anObject -] From a507fb7dc99889f9c33178708b0924427dc33b6c Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Tue, 15 Jul 2025 12:05:12 -0300 Subject: [PATCH 5/5] Add Pharo 11 , 12 & 13 to CI config --- .github/workflows/loading-groups.yml | 27 ++++++++++++++++++++------- .github/workflows/markdown-lint.yml | 7 +++++-- .github/workflows/unit-tests.yml | 17 +++++++++++------ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/.github/workflows/loading-groups.yml b/.github/workflows/loading-groups.yml index 732312b..9e14155 100644 --- a/.github/workflows/loading-groups.yml +++ b/.github/workflows/loading-groups.yml @@ -1,18 +1,31 @@ name: Baseline Groups - -on: [push,pull_request,workflow_dispatch] - +on: + - push + - pull_request + - workflow_dispatch jobs: group-loading: runs-on: ubuntu-latest strategy: fail-fast: false - matrix: - smalltalk: [ Pharo64-10 ] - load-spec: [ core, sqlite3, postgres, deployment, dependent-sunit-extensions, tests, tools, development ] + matrix: + smalltalk: + - Pharo64-10 + - Pharo64-11 + - Pharo64-12 + - Pharo64-13 + load-spec: + - core + - sqlite3 + - postgres + - deployment + - dependent-sunit-extensions + - tests + - tools + - development name: Baseline Groups / ${{ matrix.smalltalk }} + ${{ matrix.load-spec }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: hpi-swa/setup-smalltalkCI@v1 with: smalltalk-image: ${{ matrix.smalltalk }} diff --git a/.github/workflows/markdown-lint.yml b/.github/workflows/markdown-lint.yml index f59b059..55b9722 100644 --- a/.github/workflows/markdown-lint.yml +++ b/.github/workflows/markdown-lint.yml @@ -1,11 +1,14 @@ name: Markdown Lint -on: [push,pull_request,workflow_dispatch] +on: + - push + - pull_request + - workflow_dispatch jobs: remark-lint: name: runner / markdownlint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: markdownlint uses: reviewdog/action-markdownlint@v0.1 with: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 7423c8d..d88f92d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,16 +1,21 @@ name: Unit Tests - -on: [push,pull_request,workflow_dispatch] - +on: + - push + - pull_request + - workflow_dispatch jobs: build: runs-on: ubuntu-latest strategy: matrix: - smalltalk: [ Pharo64-10 ] + smalltalk: + - Pharo64-10 + - Pharo64-11 + - Pharo64-12 + - Pharo64-13 name: Unit Tests / ${{ matrix.smalltalk }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Smalltalk CI uses: hpi-swa/setup-smalltalkCI@v1 with: @@ -21,7 +26,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: name: Unit-Tests-${{matrix.smalltalk}} token: ${{ secrets.CODECOV_TOKEN }}