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

[#119] Change set-field Web-API #128

Closed
Closed
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions lib/Entry.hs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ instance A.FromJSON FieldVisibility where
"private" -> pure Private
other -> fail $ "expecting either 'public' or 'private', but found: '" <> T.unpack other <> "'"

instance FromHttpApiData FieldVisibility where
parseUrlPiece = \case
"public" -> Right Public
"private" -> Right Private
other -> Left $ "expecting either 'public' or 'private', but found: '" <> other <> "'"

newtype FieldContents = FieldContents { unFieldContents :: Text }
deriving stock (Show, Eq, Ord)
deriving newtype (Hashable, A.FromJSON, A.ToJSON, A.FromJSONKey, A.ToJSONKey)
Expand Down
16 changes: 10 additions & 6 deletions lib/Web/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ type API
:<|> "set-field"
:> RequiredParam "path" EntryPath
:> RequiredParam "field" FieldName
:>
( "private" :> Post '[JSON] Entry
:<|> "public" :> Post '[JSON] Entry
:<|> ReqBody '[JSON] (Maybe FieldContents)
:> Post '[JSON] Entry
)
:> OptionalParam "visibility" FieldVisibility
:> ReqBody '[JSON] FieldContents
:> Post '[JSON] Entry

:<|> "set-field-visibility"
:> RequiredParam "path" EntryPath
:> RequiredParam "field" FieldName
:> ( "public" :> Post '[JSON] Entry
:<|> "private" :> Post '[JSON] Entry
)

:<|> "delete-field"
:> RequiredParam "path" EntryPath
Expand Down
71 changes: 33 additions & 38 deletions lib/Web/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -125,23 +125,29 @@ reportErrors io = do
Right (Right a) -> do
return a

-- setFieldVisibility
-- :: (forall a. Command a -> Handler a)
-- -> Bool
-- -> EntryPath
-- -> FieldName
-- -> Handler Entry

makeServer
:: (SomeBackend -> (forall a. Command a -> Handler a))
-> Server API
makeServer run backend
= view (run backend)
:<|> create (run backend)
:<|>
(\txt fkey ->
private (run backend) txt fkey
:<|> public (run backend) txt fkey
:<|> set (run backend) txt fkey
)
:<|> deleteField (run backend)
:<|> find' (run backend)
:<|> rename (run backend)
:<|> copy' (run backend)
:<|> delete' (run backend)
= view (run backend)
:<|> create (run backend)
:<|> setField (run backend)
:<|> (\path field->
setFieldVisibility (run backend) path field Public
:<|> setFieldVisibility (run backend) path field Private
)
:<|> deleteField (run backend)
:<|> find' (run backend)
:<|> rename (run backend)
:<|> copy' (run backend)
:<|> delete' (run backend)
:<|>
(\path tag' ->
tag (run backend) path tag' False
Expand Down Expand Up @@ -202,44 +208,33 @@ create run coPath coForce (NewEntry coFields coTags) =

pretty = resultToText buildCreateError

private
:: (forall a. Command a -> Handler a)
-> EntryPath
-> FieldName
-> Handler Entry
private run sfoPath sfoFieldName = do
run (CmdSetField SetFieldOptions
{ sfoQPath = QualifiedPath Nothing sfoPath
, sfoFieldName
, sfoFieldContents = Nothing
, sfoVisibility = Just Private
}) >>= handleSetFieldResult

public
setFieldVisibility
:: (forall a. Command a -> Handler a)
-> EntryPath
-> FieldName
-> FieldVisibility
-> Handler Entry
public run sfoPath sfoFieldName =
setFieldVisibility run path field visibility =
run (CmdSetField SetFieldOptions
{ sfoQPath = QualifiedPath Nothing sfoPath
, sfoFieldName
{ sfoQPath = QualifiedPath Nothing path
, sfoFieldName = field
, sfoFieldContents = Nothing
, sfoVisibility = Just Public
, sfoVisibility = Just visibility
}) >>= handleSetFieldResult

set
setField
:: (forall a. Command a -> Handler a)
-> EntryPath
-> FieldName
-> Maybe FieldContents
-> Maybe FieldVisibility
-> FieldContents
-> Handler Entry
set run sfoPath sfoFieldName sfoFieldContents =
setField run path field visibility contents =
run (CmdSetField SetFieldOptions
{ sfoQPath = QualifiedPath Nothing sfoPath
, sfoFieldName
, sfoFieldContents = sfoFieldContents
, sfoVisibility = Nothing
{ sfoQPath = QualifiedPath Nothing path
, sfoFieldName = field
, sfoFieldContents = Just contents
, sfoVisibility = visibility
}) >>= handleSetFieldResult

deleteField
Expand Down
2 changes: 1 addition & 1 deletion tests/server-integration/Common/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ unit_incorrect_field_name_fromHttpApiData :: IO()
unit_incorrect_field_name_fromHttpApiData = cofferTest do
createEntry "entry"

try @HttpException ( setField "entry" "fieldname:." "contents" )
try @HttpException ( setField "entry" "fieldname:." Nothing "contents" )
>>= unwrapStatusCodeError \response bs -> do
bs @?= "Error parsing query parameter field failed: Field name can only contain the following characters: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_;'"
responseStatus response @?= status400
Expand Down
2 changes: 1 addition & 1 deletion tests/server-integration/CopyAndRename/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ copyOrRenameCreateErrors copy = cofferTest do
copyOrRenameUpdatesOnlyEntrysModificationTime :: Bool -> IO ()
copyOrRenameUpdatesOnlyEntrysModificationTime copy = cofferTest do
createEntry "entry"
response <- setField "entry" "field" "contents"
response <- setField "entry" "field" Nothing "contents"
let modifiedDate = responseBody response ^?! key "dateModified"

t1 <- getCurrentTime
Expand Down
4 changes: 2 additions & 2 deletions tests/server-integration/DeleteField/DeleteFieldTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ executeDeleteField path field =
unit_delete_field :: IO ()
unit_delete_field = cofferTest do
createEntry "entry"
void $ setField "entry" "field" "contents"
void $ setField "entry" "field" Nothing "contents"

response <- executeDeleteField "entry" "field"

Expand Down Expand Up @@ -79,7 +79,7 @@ unit_delete_field_field_not_found = cofferTest do
unit_delete_field_updates_entry's_modification_time :: IO ()
unit_delete_field_updates_entry's_modification_time = cofferTest do
createEntry "entry"
response <- setField "entry" "field" "contents"
response <- setField "entry" "field" Nothing "contents"

let oldModificationTime = responseBody response ^?! key "dateModified"

Expand Down
122 changes: 64 additions & 58 deletions tests/server-integration/SetField/SetFieldTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ module SetField.SetFieldTest where

import Control.Exception (try)
import Control.Lens
import Data.Aeson
import Data.Aeson (Value)
import Data.Aeson.Lens
import Data.Aeson.QQ.Simple (aesonQQ)
import Data.Functor (void)
import Data.Text (Text)
import Data.Time
import Network.HTTP.Req
import Network.HTTP.Types (status400, status404)
import Network.HTTP.Types (status404)
import Test.Tasty.HUnit
import Utils

unit_set_new_field :: IO ()
unit_set_new_field = cofferTest do
createEntry "entry"

response <- setField "entry" "newField" "contents"
response <- setField "entry" "newField" Nothing "contents"
response @=
[aesonQQ|
{
Expand All @@ -41,26 +42,16 @@ unit_set_new_field = cofferTest do
unit_set_field_private :: IO ()
unit_set_field_private = cofferTest do
createEntry "entry"
setField "entry" "public-field" "contents"
response <-
executeCommand
POST
["set-field", "private"]
NoReqBody
(jsonResponse @Value)
( mconcat
[ "path" =: ("entry" :: Text)
, "field" =: ("public-field" :: Text)
]
)

response <- setField "entry" "private-field" (Just False) "contents"

let fieldMb =
responseBody response
& scrubDates
& findField "public-field"
& findField "private-field"

case fieldMb of
Nothing -> assertFailure $ "Expected field \"public-field\", found nothing"
Nothing -> assertFailure $ "Expected field \"private-field\", found nothing"
Just value ->
value @?=
[aesonQQ|
Expand All @@ -73,18 +64,9 @@ unit_set_field_private = cofferTest do

unit_set_field_public :: IO ()
unit_set_field_public = cofferTest do
unit_set_field_private
response <-
executeCommand
POST
["set-field", "public"]
NoReqBody
(jsonResponse @Value)
( mconcat
[ "path" =: ("entry" :: Text)
, "field" =: ("public-field" :: Text)
]
)
createEntry "entry"

response <- setField "entry" "public-field" (Just True) "contents"

let fieldMb =
responseBody response
Expand All @@ -105,7 +87,7 @@ unit_set_field_public = cofferTest do

unit_set_field_entry_not_found :: IO ()
unit_set_field_entry_not_found = cofferTest do
try @HttpException (setField "entry" "field" "contents") >>=
try @HttpException (setField "entry" "field" Nothing "contents") >>=
processStatusCodeError status404
[aesonQQ|
[
Expand All @@ -116,40 +98,16 @@ unit_set_field_entry_not_found = cofferTest do
]
|]

unit_set_field_missing_field_contents :: IO ()
unit_set_field_missing_field_contents = cofferTest do
createEntry "entry"

try @HttpException
(
executeCommand
POST
["set-field"]
(ReqBodyJson Null)
(jsonResponse @Value)
( mconcat
[ "path" =: ("entry" :: Text)
, "field" =: ("field" :: Text)
]
)
) >>=
processStatusCodeError status400
[aesonQQ|
[
{
"error": "The entry at '/entry' does not yet have a field 'field'.\nIn order to create a new field, please include 'FIELDCONTENTS' in the body.",
"code": 301
}
]
|]
-- unit_set_field_missing_field_contents :: IO ()
-- TODO: add test that expects an error in case of missing contents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems as unsolved TODO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved now, you can check how I did it, I'm not sure it is the proper way


unit_set_field_updates_modification_date :: IO ()
unit_set_field_updates_modification_date = cofferTest do
createEntry "entry"
setField "entry" "field" "contents"
setField "entry" "field" Nothing "contents"

t1 <- getCurrentTime
response <- setField "entry" "field" "contents2"
response <- setField "entry" "field" Nothing "contents2"
t2 <- getCurrentTime

let newModifiedDate = responseBody response ^?! key "dateModified" . _JSON @_ @UTCTime
Expand All @@ -158,3 +116,51 @@ unit_set_field_updates_modification_date = cofferTest do

assertBool "Dates are not equal"
$ responseBody response ^?! key "fields" . key "field" . key "dateModified" . _JSON == newModifiedDate

unit_set_field_visibility :: IO ()
unit_set_field_visibility = cofferTest do
createEntry "dir/entry"
void $ setField "dir/entry" "private-field" Nothing "contents"
void $ setField "dir/entry" "public-field" Nothing "contents"
changeFieldVisibility "dir/entry" "private-field" "private"
changeFieldVisibility "dir/entry" "public-field" "public"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why there is two checks in one test?

If we're expecting to fail at once = one way (only private/public)
If it could fail on one, but not on second variant seems as two separate tests

I know that they're just doing the same thing, but we aren't short on test space)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed 👍


response <-
executeCommand
GET
["view"]
NoReqBody
(jsonResponse @Value)
("path" =: (("dir/entry") :: Text))

response @=
[aesonQQ|
{
"entries": [],
"subdirs": {
"dir": {
"entries": [
{
"path": "/dir/entry",
"dateModified": "",
"masterField": null,
"fields": {
"public-field": {
"dateModified": "",
"visibility": "public",
"contents": "contents"
},
"private-field": {
"dateModified": "",
"visibility": "private",
"contents": "contents"
}
},
"tags": []
}
],
"subdirs": {}
}
}
}
|]
2 changes: 1 addition & 1 deletion tests/server-integration/Tag/TagTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ unit_tag_duplicate_tag = cofferTest do
unit_tag_updates_only_entry's_modification_time :: IO ()
unit_tag_updates_only_entry's_modification_time = cofferTest do
createEntry "entry"
response <- setField "entry" "field" "contents"
response <- setField "entry" "field" Nothing "contents"
let modificationTime = responseBody response ^?! key "dateModified"
t1 <- getCurrentTime
response <- addOrRemoveTag POST "entry" "tag"
Expand Down
Loading