tmcgilchrist / airship Goto Github PK
View Code? Open in Web Editor NEWHelium + Webmachine = Airship. A toolkit for building declarative, RESTful web apps.
Home Page: http://tmcgilchrist.github.io/airship/
Helium + Webmachine = Airship. A toolkit for building declarative, RESTful web apps.
Home Page: http://tmcgilchrist.github.io/airship/
And associated decision point G11.
Resource functions like serviceAvailable
, allowMissingPost
, and forbidden
(there are more) should be updated to return specific types like data ServiceAvailable = Available | Unavailable
, so that users don't have to think about the meaning of a particular boolean.
Currently you have to provide a Content-Type if you want the processPost
function ever to engage a *
or unprovided Content-Type. Need to fix this for an API endpoint.
It's possibly not a good idea to alway provide this header for services, exposing extra information about how the server is making decisions about what to return back.
There's 2 good solutions to make this optional:
resourceToWai
and toWaiResponse
Which option would you favour as the suggested solution for Airship?
Have you run into something similar with using Airship for applications?
Happy to provide some code or examples for both options.
> /tmp/stackage-build8$ stack unpack airship-0.5.0
Unpacked airship-0.5.0 to /tmp/stackage-build8/airship-0.5.0/
> /tmp/stackage-build8/airship-0.5.0$ runghc -clear-package-db -global-package-db -package-db=/home/stackage/work/builds/nightly/pkgdb Setup configure --package-db=clear --package-db=global --package-db=/home/stackage/work/builds/nightly/pkgdb --libdir=/home/stackage/work/builds/nightly/lib --bindir=/home/stackage/work/builds/nightly/bin --datadir=/home/stackage/work/builds/nightly/share --libexecdir=/home/stackage/work/builds/nightly/libexec --sysconfdir=/home/stackage/work/builds/nightly/etc --docdir=/home/stackage/work/builds/nightly/doc/airship-0.5.0 --htmldir=/home/stackage/work/builds/nightly/doc/airship-0.5.0 --haddockdir=/home/stackage/work/builds/nightly/doc/airship-0.5.0 --flags=
Configuring airship-0.5.0...
> /tmp/stackage-build8/airship-0.5.0$ runghc -clear-package-db -global-package-db -package-db=/home/stackage/work/builds/nightly/pkgdb Setup build
Building airship-0.5.0...
Preprocessing library airship-0.5.0...
[ 1 of 13] Compiling Airship.Internal.Date ( src/Airship/Internal/Date.hs, dist/build/Airship/Internal/Date.o )
[ 2 of 13] Compiling Airship.Types ( src/Airship/Types.hs, dist/build/Airship/Types.o )
[ 3 of 13] Compiling Airship.Internal.Parsers ( src/Airship/Internal/Parsers.hs, dist/build/Airship/Internal/Parsers.o )
[ 4 of 13] Compiling Airship.Headers ( src/Airship/Headers.hs, dist/build/Airship/Headers.o )
[ 5 of 13] Compiling Airship.Resource ( src/Airship/Resource.hs, dist/build/Airship/Resource.o )
src/Airship/Resource.hs:131:58: error:
• Couldn't match type ‘forall a. Maybe a’ with ‘Maybe ETag’
Expected type: Webmachine m (Maybe ETag)
Actual type: Webmachine m (forall a. Maybe a)
• In the ‘generateETag’ field of a record
In the expression:
Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, methodHead],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
In an equation for ‘defaultResource’:
defaultResource
= Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, ....],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
src/Airship/Resource.hs:136:58: error:
• Couldn't match type ‘forall a. Maybe a’ with ‘Maybe UTCTime’
Expected type: Webmachine m (Maybe UTCTime)
Actual type: Webmachine m (forall a. Maybe a)
• In the ‘lastModified’ field of a record
In the expression:
Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, methodHead],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
In an equation for ‘defaultResource’:
defaultResource
= Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, ....],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
src/Airship/Resource.hs:139:58: error:
• Couldn't match type ‘forall a. Maybe a’ with ‘Maybe ByteString’
Expected type: Webmachine m (Maybe ByteString)
Actual type: Webmachine m (forall a. Maybe a)
• In the ‘movedPermanently’ field of a record
In the expression:
Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, methodHead],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
In an equation for ‘defaultResource’:
defaultResource
= Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, ....],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
src/Airship/Resource.hs:140:58: error:
• Couldn't match type ‘forall a. Maybe a’ with ‘Maybe ByteString’
Expected type: Webmachine m (Maybe ByteString)
Actual type: Webmachine m (forall a. Maybe a)
• In the ‘movedTemporarily’ field of a record
In the expression:
Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, methodHead],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
In an equation for ‘defaultResource’:
defaultResource
= Resource
{allowMissingPost = return False,
allowedMethods = return [methodOptions, methodGet, ....],
contentTypesAccepted = return [],
contentTypesProvided = return [("text/html", halt status405)],
deleteCompleted = return False, deleteResource = return False,
entityTooLarge = return False, forbidden = return False,
generateETag = return Nothing, implemented = return True,
isAuthorized = return True, isConflict = return False,
knownContentType = return True, lastModified = return Nothing,
languageAvailable = return True, malformedRequest = return False,
movedPermanently = return Nothing,
movedTemporarily = return Nothing, multipleChoices = return False,
patchContentTypesAccepted = return [],
previouslyExisted = return False,
processPost = return (PostProcess []),
resourceExists = return True, serviceAvailable = return True,
uriTooLong = return False, validContentHeaders = return True}
Currently the .cabal file specifies cryptohash ==0.11.6.*
I'm thinking it'd be something like
uncaughtException :: SomeException -> Handler s m ()
which would then be used in an invocation of catch
during the decision tree's evalState
call.
Right now, having a resource that responds to both GET and PATCH requests is difficult. We need to institute a processPatch
function that operates on principles similar to processPost
.
I had a resource defined with:
contentTypesProvided = return [("*/*", return Empty)]
But airship was unable to handle it. Any requests with specific Accept
headers resulted in 406 Not Acceptable
being returned. The only case where it would work is if I passed along the exact string */*
.
I think what should happen is that regardless of the value of the Accept
header this should match and execute the function.
Should be a fun little exercise.
Would be good to remove the use of partial functions like fromJust
from the Decision module and anywhere else really.
Don't have the time right now to dig into it, but flagging as something to do.
I haven't looked into it deeply but I assume;
500
errorThe current setup has a single State
type, if you choose to use it, across all resources. So you end up with a State type that knows about all the various resources and what they want to store. Plus when you put or ask for the State, you need to know about the other values. Clearly not ideal.
e.g.
data State = State { resource1 :: Text, resource2 :: Int }
Ideally each resource would have private state, which got run per-resource. I've got no specific ideas right now, suggestions welcome.
This is more a query about the types of allowedMethods
, contentTypesAccepted
[1] and possibly contentTypesProvided
. At the moment each of them is wrapped in a Handler s m a
. I'm wondering why they can't just be say Bool
or [(MediaType, Handler s m ())]
. It doesn't seem like you'd want either of them to dynamic or perhaps I'm missing a use-case where you do.
We are thinking if they're not wrapped in Handler s m a
we could generate nice API docs from each of the resources.
[1]https://github.com/helium/airship/blob/master/src/Airship/Resource.hs#L37-L46
Probably consult the original Erlang port to see how they do it.
https://github.com/helium/airship/blob/master/src/Airship/Internal/Decision.hs#L675-L680
When the response is negotiated based on the Accept headers the Vary
header is not set.
There is a comment in g07
about setting Vary
but I don't think you'd set Vary
for that specific use case. As a general rule you want to append any request header that is examined and results in a logic branch that affects the response.
You get nightly builds, notification of failures & inclusion in the LTS stackage distribution.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.