Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed key from API and added sample model with tests. #148 #149

Merged
merged 4 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Morphir/IR/SDK.elm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Morphir.IR.SDK.List as List
import Morphir.IR.SDK.Maybe as Maybe
import Morphir.IR.SDK.Regex as Regex
import Morphir.IR.SDK.Result as Result
import Morphir.IR.SDK.StatefulApp as StatefulApp
import Morphir.IR.SDK.String as String
import Morphir.IR.SDK.Tuple as Tuple

Expand All @@ -49,5 +50,6 @@ packageSpec =
, ( [ [ "list" ] ], List.moduleSpec )
, ( [ [ "tuple" ] ], Tuple.moduleSpec )
, ( [ [ "regex" ] ], Regex.moduleSpec )
, ( [ [ "stateful", "app" ] ], StatefulApp.moduleSpec )
]
}
47 changes: 47 additions & 0 deletions src/Morphir/IR/SDK/StatefulApp.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{-
Copyright 2020 Morgan Stanley

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-}


module Morphir.IR.SDK.StatefulApp exposing (..)

import Dict
import Morphir.IR.Documented exposing (Documented)
import Morphir.IR.Module as Module exposing (ModulePath)
import Morphir.IR.Name as Name
import Morphir.IR.Path as Path exposing (Path)
import Morphir.IR.Type as Type exposing (Specification(..), Type(..))
import Morphir.IR.Value as Value


moduleName : ModulePath
moduleName =
Path.fromString "StatefulAp"


moduleSpec : Module.Specification () ()
moduleSpec =
{ types =
Dict.fromList
[ ( Name.fromString "StatefulApp"
, CustomTypeSpecification [ Name.fromString "k", Name.fromString "c", Name.fromString "s", Name.fromString "e" ]
[ Type.Constructor (Name.fromString "StatefulApp") []
]
|> Documented "Type that represents a stateful app."
)
]
, values =
Dict.empty
}
36 changes: 21 additions & 15 deletions src/Morphir/SDK/StatefulApp.elm
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
{-
Copyright 2020 Morgan Stanley
Copyright 2020 Morgan Stanley

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-}


module Morphir.SDK.StatefulApp exposing (StatefulApp)
module Morphir.SDK.StatefulApp exposing (StatefulApp(..))

{-| Utilities for modeling stateful applications.
{-| API for modeling stateful applications.

@docs StatefulApp

-}


{-| Type that represents a stateful application.
{-| Type that represents a stateful application. The following type parameters allow you to fit it to your use case:

- **k** - Key that's used to partition commands, events and state.
- **c** - Type that defines all the commands accepted by the application.
- **s** - Type that defines the state managed by the application.
- **e** - Type that defines all the events published by the application.

-}
type alias StatefulApp k c s e =
{ businessLogic : k -> Maybe s -> c -> ( k, Maybe s, e ) }
type StatefulApp k c s e
= StatefulApp (Maybe s -> c -> ( Maybe s, e ))
5 changes: 4 additions & 1 deletion tests-integration/reference-model/elm.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
{
"type": "application",
"source-directories": [
"../../src",
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0"
"elm/html": "1.0.0",
"elm-explorations/test": "1.2.2"
},
"indirect": {
"elm/json": "1.1.3",
"elm/random": "1.0.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2"
Expand Down
3 changes: 2 additions & 1 deletion tests-integration/reference-model/morphir.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"sourceDirectory": "src",
"exposedModules": [
"Types",
"Values"
"Values",
"BooksAndRecords"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module Morphir.Reference.Model.BooksAndRecords exposing (..)

import Morphir.SDK.StatefulApp exposing (StatefulApp(..))



{- Type aliases for modeling in the language of the business -}


type alias ID =
String


type alias ProductID =
String


type alias Price =
Float


type alias Quantity =
Int



{- Identifies a structure that can be associated to a persistance entity -}


type alias Deal =
{ product : ProductID
, price : Price
, quantity : Quantity
}



{- These define the requests that can be made of this service -}


type DealCmd
= OpenDeal ProductID Price Quantity
| CloseDeal



{- These define the responses that would result from requests -}


type DealEvent
= DealOpened ProductID Price Quantity
| DealClosed
| InvalidQuantity Quantity
| InvalidPrice Price
| DuplicateDeal
| DealNotFound



{- Defines that this is a stateful application that uses ID as the entity key (for possible partioning),
accepts requests of type DealCmd,
manages data in the form of a Deal,
and produces events of type DealEvent.

Note that there's no indication of whether the API is synchronous or asynchronous. That's up to the implementation to decide.
-}
{- Defines the business logic of this app.
That is whether or not to accept a request to open or close a deal.
-}


logic : Maybe Deal -> DealCmd -> ( Maybe Deal, DealEvent )
logic dealState dealCmd =
-- Act accordingly based on whether the deal already exists.
case dealState of
Just _ ->
case dealCmd of
CloseDeal ->
( Nothing, DealClosed )

OpenDeal _ _ _ ->
( dealState, DuplicateDeal )

Nothing ->
case dealCmd of
OpenDeal productId price qty ->
if price < 0 then
( dealState, InvalidPrice price )

else if qty < 0 then
( dealState, InvalidQuantity qty )

else
( Deal productId price qty |> Just
, DealOpened productId price qty
)

CloseDeal ->
( dealState, DealNotFound )


app : StatefulApp ID DealCmd Deal DealEvent
app =
StatefulApp logic
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module Morphir.Reference.Model.BooksAndRecordsTests exposing (..)

import Expect
import Morphir.Reference.Model.BooksAndRecords as BooksAndRecords exposing (..)
import Morphir.SDK.StatefulApp exposing (StatefulApp)
import Test exposing (Test, test)


scenarios : Test
scenarios =
let
id =
""

qty =
100

price =
123
in
BooksAndRecords.app
|> scenario "Open and close"
{ givenState = Nothing
, whenCommandsReceived =
[ OpenDeal id price qty
, CloseDeal
]
, thenExpect =
{ state = Nothing
, events =
[ DealOpened id price qty
, DealClosed
]
}
}


type alias TestScenario c s e =
{ givenState : Maybe s
, whenCommandsReceived : List c
, thenExpect :
{ state : Maybe s
, events : List e
}
}


scenario : String -> TestScenario c s e -> StatefulApp k c s e -> Test
scenario name s (StatefulApp businessLogic) =
test name
(\_ ->
s.whenCommandsReceived
|> List.foldl
(\nextCommand ( state, events ) ->
let
( nextState, newEvent ) =
businessLogic state nextCommand
in
( nextState, List.append events [ newEvent ] )
)
( s.givenState, [] )
|> Expect.equal
( s.thenExpect.state, s.thenExpect.events )
)