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

Derive cascade-core package from cascade-api and cascade-prelude #44

Draft
wants to merge 72 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
fd081f6
feat(cascade-prelude): add Validatable typeclass
the-dr-lazy Feb 19, 2021
09a6996
fix(cascade-prelude): add fieldName in error type
the-dr-lazy Feb 20, 2021
1fee640
feat(cascade-prelude): instance ToJSON GenericValidationErrors
the-dr-lazy Feb 20, 2021
d77b76a
refactor(cascade-prelude): rename GValidatable class
the-dr-lazy Feb 22, 2021
220c278
fix(cascade-prelude): infinite typeclass loop
the-dr-lazy Feb 22, 2021
10c2a4e
refactor(cascade-prelude): rename Validity type
the-dr-lazy Feb 22, 2021
2206c73
feat(cascade-prelude): arbitrary instance FromJSON GenericValidationE…
the-dr-lazy Feb 23, 2021
3d61457
chore(cascade-prelude): enable DeriveDataTypeable
the-dr-lazy Feb 23, 2021
a94517d
feat(cascade-api): Username generic validation
the-dr-lazy Feb 23, 2021
d220b70
feat(cascade-api): EmailAddress generic validation
the-dr-lazy Feb 23, 2021
f182192
feat(cascade-api): Password generic validation
the-dr-lazy Feb 23, 2021
dc1a12d
feat(cascade-api): User.Creatable generic validation
the-dr-lazy Feb 23, 2021
3219904
ci: disable coverage
the-dr-lazy Feb 23, 2021
738eb95
ci: fix workflow syntax issue
the-dr-lazy Feb 23, 2021
0578687
refactor(cascade-api): Authentication.Credential generic validation
the-dr-lazy Feb 23, 2021
1c033f3
feat: make Validatable typeclass multi-param
the-dr-lazy Feb 25, 2021
0971fda
refactor(cascade-api): remove redundant comment
the-dr-lazy Feb 26, 2021
0c19661
style(cascade-prelude): run brittany
jedimahdi Mar 10, 2021
dc24403
feat(cascade-prelude): add Maybe instance to Validatable
jedimahdi Mar 10, 2021
630a65b
feat(cascade-prelude): implement Validatable instance for Text.NonEmpty
jedimahdi Mar 10, 2021
6f74826
feat(cascade-api): implement Validatable instance for Deadline
jedimahdi Mar 10, 2021
0a50e53
fix(cascade-api): update task with new validation
jedimahdi Mar 10, 2021
17d1f9a
feat(cascade-api): add title data type and refactor
jedimahdi Mar 17, 2021
bd5c47b
feat: new generic record validation
the-dr-lazy Mar 22, 2021
1a0d6f4
Merge remote-tracking branch 'origin/next' into feature/generic-valid…
the-dr-lazy Mar 22, 2021
10da6aa
Revert "ci: fix workflow syntax issue"
the-dr-lazy Mar 22, 2021
9eafeaa
Revert "ci: disable coverage"
the-dr-lazy Mar 22, 2021
53dded0
refactor(cascade-prelude): reduce D1, S1, C1 instances to M1 in gener…
the-dr-lazy Mar 22, 2021
aa79bc9
feat(cascade-core): init
the-dr-lazy Mar 18, 2021
edd18b8
feat(cascade-core): introduce new Id type
the-dr-lazy Mar 22, 2021
9154f0d
chore: delete cabal files
the-dr-lazy Mar 22, 2021
18994a1
Merge branch 'feature/generic-validation' into feature/cascade-core
the-dr-lazy Mar 22, 2021
d703119
feat(cascade-core): new Id type
the-dr-lazy Mar 22, 2021
8a30c46
feat(cascade-core): new Username type
the-dr-lazy Mar 23, 2021
695a0c1
feat(cascade-core): new EmailAddress type
the-dr-lazy Mar 23, 2021
6d14579
feat(cascade-core): move ScryptL effect to core package
the-dr-lazy Mar 23, 2021
718986e
feat(cascade-core): new Password type
the-dr-lazy Mar 23, 2021
6204d51
feat(cascade-core): export types in common Data module
the-dr-lazy Mar 23, 2021
8b52de8
feat(cascade-core): introduce User model type
the-dr-lazy Mar 23, 2021
867a0af
refactor: type variables renaming
the-dr-lazy Mar 24, 2021
e52461c
feat(cascade-core): add Slug type
the-dr-lazy Mar 24, 2021
480cb3d
feat(cascade-prelude): add Text.Finite type
the-dr-lazy Mar 25, 2021
2d41993
feat(cascade-core): move Slug type into internal modules
the-dr-lazy Mar 25, 2021
1f1cef0
feat(cascade-core): export Slug type in Cascade.Core.Data
the-dr-lazy Mar 25, 2021
f7b682e
style: format boot files
the-dr-lazy Mar 25, 2021
0495597
feat(cascade-core): add Project model type
the-dr-lazy Mar 25, 2021
71d4e67
feat(cascade-core): add Task model type
the-dr-lazy Mar 25, 2021
696f3bd
fix(cascade-core): make user and project phase persisted in model
the-dr-lazy Mar 26, 2021
4151d83
fix(cascade-core): use persisted label in Task.Unit
the-dr-lazy Mar 26, 2021
b276373
feat(cascade-core): restructure model datatypes
the-dr-lazy Mar 29, 2021
bcc73e5
refactor(cascade-core): composition over application
the-dr-lazy Mar 29, 2021
569be3f
feat(cascade-core): add cwt to user model
the-dr-lazy Mar 30, 2021
b2715c1
fix(cascade-core): module name
the-dr-lazy Apr 1, 2021
27abfee
feat(cascade-core): move database contracts from API
the-dr-lazy Apr 10, 2021
c605bb7
feat(cascade-core): redesign Id datatype
the-dr-lazy Apr 10, 2021
3cf76b5
fix(cascade-core): broken module import
the-dr-lazy Apr 10, 2021
12630d5
refactor(cascade-core): rename validation error data constructors
the-dr-lazy Apr 10, 2021
f6a509b
refactor(cascade-core): remove unnecessary conversions
the-dr-lazy Apr 10, 2021
88c9773
feat(cascade-core): make task planned pomodoro optional
the-dr-lazy Apr 11, 2021
559b792
feat(cascade-core): define evidences for user
the-dr-lazy Apr 11, 2021
5dd002d
feat(cascade-core): move Time effect module
the-dr-lazy Apr 11, 2021
d0f8b13
feat(cascade-core): move Database effect module
the-dr-lazy Apr 11, 2021
6879d48
feat(cascade-core): add Id effect
the-dr-lazy Apr 11, 2021
564bfa1
feat(cascade-core): effects re-export module
the-dr-lazy Apr 11, 2021
c96bd9e
feat(cascade-core): remove unnecessary timing fields from model
the-dr-lazy Apr 11, 2021
da5d382
refactor(cascade-core): redundant imports
the-dr-lazy Apr 12, 2021
4f3cf0d
feat(cascade-core): Hashed.coMk function
the-dr-lazy Apr 13, 2021
a631312
feat(cascade-core): make Id types nominal on phase
the-dr-lazy Apr 13, 2021
0f87d9a
feat(cascade-core): make EmailAddress type nominal on phase
the-dr-lazy Apr 13, 2021
f841aeb
chore: upgrade relude
the-dr-lazy Apr 13, 2021
85a810d
feat(cascade-core): add Repository.User effect
the-dr-lazy Apr 13, 2021
c1e0c5c
chore: upgrade relude
the-dr-lazy Apr 13, 2021
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: 5 additions & 1 deletion cabal.project
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
packages: cascade-prelude, cascade-api, cascade-cli
packages:
cascade-prelude
, cascade-api
, cascade-cli
, cascade-core
optimization: False
documentation: True

Expand Down
2 changes: 1 addition & 1 deletion cabal.project.freeze
Original file line number Diff line number Diff line change
Expand Up @@ -1854,7 +1854,7 @@ constraints: AC-Angle ==1.0,
relational-query-HDBC ==0.7.2.0,
relational-record ==0.2.2.0,
relational-schemas ==0.1.8.0,
relude ==0.7.0.0,
relude ==1.0.0.1,
renderable ==0.2.0.1,
replace-attoparsec ==1.4.4.0,
replace-megaparsec ==1.4.4.0,
Expand Down
1 change: 1 addition & 0 deletions cascade-api/package.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let cascade-api =
[ "attoparsec"
, "beam-core"
, "beam-postgres"
, "cascade-core"
, "chronos"
, "either"
, "email-validate"
Expand Down
2 changes: 1 addition & 1 deletion cascade-api/src/Cascade/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import qualified Cascade.Api.Effect.Database.Project
as Database.Project
import qualified Cascade.Api.Effect.Database.Task as Database.Task
import qualified Cascade.Api.Effect.Database.User as Database.User
import qualified Cascade.Api.Effect.Scrypt as Scrypt
import qualified Cascade.Api.Effect.Time as Time
import Cascade.Api.Network.Wai.Application
import Cascade.Api.Orphans ( )
import qualified Cascade.Core.Effect.Scrypt as Scrypt
import qualified Database.PostgreSQL.Simple as Postgres
import qualified Network.Wai.Handler.Warp as Warp
import Polysemy ( runFinal )
Expand Down
32 changes: 32 additions & 0 deletions cascade-api/src/Cascade/Api/Data/Aeson/FieldErrorFormat.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{-|
Module : Cascade.Api.Data.Aeson.FieldErrorFormat
Description : !!! INSERT MODULE SHORT DESCRIPTION !!!
Copyright : (c) 2020-2021 Cascade
License : MPL 2.0
Maintainer : Mohammad Hasani <[email protected]> (the-dr-lazy.github.io)
Stability : Stable
Portability : POSIX

!!! INSERT MODULE LONG DESCRIPTION !!!
-}

{-# LANGUAGE UndecidableInstances #-}

module Cascade.Api.Data.Aeson.FieldErrorFormat (FieldErrorFormat(..)) where

import qualified Data.Aeson as Aeson
import Data.Aeson ( FromJSON
, ToJSON
)
import GHC.Generics ( Rep )

newtype FieldErrorFormat (error :: Type) = FieldErrorFormat error

options :: Aeson.Options
options = Aeson.defaultOptions { Aeson.tagSingleConstructors = True, Aeson.omitNothingFields = True }

instance (Generic error, Aeson.GToJSON Aeson.Zero (Rep error)) => ToJSON (FieldErrorFormat error) where
toJSON (FieldErrorFormat e) = Aeson.genericToJSON options e

instance (Generic error, Aeson.GFromJSON Aeson.Zero (Rep error)) => FromJSON (FieldErrorFormat error) where
parseJSON = fmap FieldErrorFormat . Aeson.genericParseJSON options
32 changes: 32 additions & 0 deletions cascade-api/src/Cascade/Api/Data/Aeson/RecordErrorFormat.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{-|
Module : Cascade.Api.Data.Aeson.RecordErrorFormat
Description : !!! INSERT MODULE SHORT DESCRIPTION !!!
Copyright : (c) 2020-2021 Cascade
License : MPL 2.0
Maintainer : Mohammad Hasani <[email protected]> (the-dr-lazy.github.io)
Stability : Stable
Portability : POSIX

!!! INSERT MODULE LONG DESCRIPTION !!!
-}

{-# LANGUAGE UndecidableInstances #-}

module Cascade.Api.Data.Aeson.RecordErrorFormat (RecordErrorFormat(..)) where

import qualified Data.Aeson as Aeson
import Data.Aeson ( FromJSON
, ToJSON
)
import GHC.Generics ( Rep )

newtype RecordErrorFormat (error :: Type) = RecordErrorFormat error

options :: Aeson.Options
options = Aeson.defaultOptions { Aeson.omitNothingFields = True }

instance (Generic error, Aeson.GToJSON Aeson.Zero (Rep error)) => ToJSON (RecordErrorFormat error) where
toJSON (RecordErrorFormat e) = Aeson.genericToJSON options e

instance (Generic error, Aeson.GFromJSON Aeson.Zero (Rep error)) => FromJSON (RecordErrorFormat error) where
parseJSON = fmap RecordErrorFormat . Aeson.genericParseJSON options
35 changes: 18 additions & 17 deletions cascade-api/src/Cascade/Api/Data/Authentication.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,33 @@ Portability : POSIX
!!! INSERT MODULE LONG DESCRIPTION !!!
-}

module Cascade.Api.Data.Authentication (RawCredential(..), ParsedCredential, parseRawCredential) where
{-# LANGUAGE UndecidableInstances #-}
module Cascade.Api.Data.Authentication (Credential(..), parseRawCredential) where

import qualified Cascade.Api.Data.Aeson.RecordErrorFormat
as Aeson
import qualified Cascade.Api.Data.ByteString.Password
as Password
import qualified Cascade.Api.Data.Text.Username as Username
import qualified Cascade.Api.Data.User as User
import Cascade.Data.Validation ( Validate
, Validation
)
import qualified Cascade.Data.Validation as Validation
import Data.Aeson ( FromJSON
, ToJSON
)
import Validation

data RawCredential = RawCredential
{ username :: Text
, password :: Text
-- brittany-disable-next-binding
data Credential (p :: Validation.Phase) = Credential
{ username :: Validate p Text User.Username
, password :: Validate p Text User.Password
}
deriving stock (Generic, Show, Eq)
deriving anyclass (FromJSON, ToJSON)
deriving stock Generic

data ParsedCredential = ParsedCredential
{ username :: User.Username
, password :: User.Password
}
deriving stock (Generic, Show, Eq)
deriving stock instance Show (Credential 'Validation.Raw)
deriving via Aeson.RecordErrorFormat (Credential 'Validation.Raw) instance ToJSON (Credential 'Validation.Raw)
deriving via Aeson.RecordErrorFormat (Credential 'Validation.Raw) instance FromJSON (Credential 'Validation.Raw)

parseRawCredential :: RawCredential -> Validation () ParsedCredential
parseRawCredential RawCredential {..} =
let validateUsername = Username.mk username |> first mempty
validatePassword = Password.mk password |> first mempty
in ParsedCredential <$> validateUsername <*> validatePassword
parseRawCredential :: Credential 'Validation.Raw -> Validation () (Credential 'Validation.Parsed)
parseRawCredential = first mempty . Validation.parseRecord Credential { username = Username.mk, password = Password.mk }
22 changes: 14 additions & 8 deletions cascade-api/src/Cascade/Api/Data/ByteString/Password.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ Portability : POSIX
!!! INSERT MODULE LONG DESCRIPTION !!!
-}

module Cascade.Api.Data.ByteString.Password (Password, pattern Password, ValidationError(..), ValidationErrors, mk, un) where
module Cascade.Api.Data.ByteString.Password (Password, pattern Password, Error(..), Errors, mk, un) where

import qualified Cascade.Api.Data.Aeson.FieldErrorFormat
as Aeson
import Cascade.Data.Validation ( Validation )
import qualified Cascade.Data.Validation as Validation
import Control.Selective ( ifS )
import Data.Aeson ( FromJSON
, ToJSON
)
import qualified Data.Text as Text
import Validation

newtype Password = Mk
{ un :: ByteString }
Expand All @@ -27,16 +30,19 @@ pattern Password :: ByteString -> Password
pattern Password a <- Mk a
{-# COMPLETE Password #-}

data ValidationError
data Error
= IsEmpty
| IsShort
deriving stock (Generic, Show)
deriving anyclass (FromJSON, ToJSON)
deriving ToJSON via Aeson.FieldErrorFormat Error
deriving FromJSON via Aeson.FieldErrorFormat Error

type ValidationErrors = NonEmpty ValidationError
type Errors = NonEmpty Error

mk :: Text -> Validation ValidationErrors Password
type instance Validation.Errors Text Password = Errors

mk :: Text -> Validation Errors Password
mk input = Mk (encodeUtf8 input) <$ validate input

validate :: Text -> Validation ValidationErrors ()
validate input = ifS (pure $ Text.null input) (failure IsEmpty) (failureIf (Text.length input < 8) IsShort)
validate :: Text -> Validation Errors ()
validate input = ifS (pure $ Text.null input) (Validation.failure IsEmpty) (Validation.failureIf (Text.length input < 8) IsShort)
38 changes: 34 additions & 4 deletions cascade-api/src/Cascade/Api/Data/OffsetDatetime/Deadline.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,39 @@ Portability : POSIX
!!! INSERT MODULE LONG DESCRIPTION !!!
-}

module Cascade.Api.Data.OffsetDatetime.Deadline (Deadline, un, mk) where
module Cascade.Api.Data.OffsetDatetime.Deadline (Deadline, un, mk, parse) where

import Cascade.Data.Chronos.Future
import Chronos ( OffsetDatetime )
import qualified Cascade.Api.Data.Aeson.FieldErrorFormat
as Aeson
import Cascade.Api.Data.OffsetDatetime ( FormattedOffsetDatetime )
import Cascade.Data.Chronos.Future ( Future )
import qualified Cascade.Data.Chronos.Future as Future
import Cascade.Data.Validation ( Validation )
import qualified Cascade.Data.Validation as Validation
import Chronos ( OffsetDatetime
, Time
)
import Data.Aeson ( FromJSON
, ToJSON
)

type Deadline = Future OffsetDatetime
newtype Deadline = Deadline (Future OffsetDatetime)
deriving stock Show

un :: Deadline -> OffsetDatetime
un (Deadline future) = Future.un future

mk :: Time -> OffsetDatetime -> Maybe Deadline
mk now input = Deadline <$> Future.mk input now

data Error = IsPast
deriving stock (Generic, Show)
deriving (ToJSON, FromJSON) via Aeson.FieldErrorFormat Error

type Errors = NonEmpty Error

type instance Validation.Errors OffsetDatetime Deadline = Errors
type instance Validation.Errors FormattedOffsetDatetime Deadline = Errors

parse :: Time -> OffsetDatetime -> Validation Errors Deadline
parse now = Validation.maybeToSuccess (IsPast :| []) . mk now
Loading