diff --git a/bower.json b/bower.json index e4276c1..33ff15a 100644 --- a/bower.json +++ b/bower.json @@ -25,10 +25,13 @@ "purescript-nonempty": "^4.1.1", "purescript-numbers": "^5.0.0", "purescript-oidc-crypt-utils": "^7.0.1", - "purescript-pathy": "^4.1.0", - "purescript-uri": "^4.2.3", - "purescript-sql-squared": "^0.10.0", - "purescript-const": "^3.2.0" + "purescript-pathy": "^5.0.0", + "purescript-string-parsers": "^3.0.0", + "purescript-strings": "^3.5.0", + "purescript-uri": "^5.0.0", + "purescript-sql-squared": "^0.11.0", + "purescript-const": "^3.2.0", + "purescript-codec": "^2.1.0" }, "devDependencies": { "purescript-assert": "^3.0.0", @@ -39,5 +42,8 @@ "purescript-quickcheck": "^4.6.1", "purescript-quasar-test-starter": "slamdata/quasar-test-starter#^1.0.0", "purescript-quickcheck-laws": "^3.0.1" + }, + "resolutions": { + "purescript-pathy": "refactor" } } diff --git a/src/Quasar/Advanced/Paths.purs b/src/Quasar/Advanced/Paths.purs index 3a378b4..b983535 100644 --- a/src/Quasar/Advanced/Paths.purs +++ b/src/Quasar/Advanced/Paths.purs @@ -16,31 +16,35 @@ limitations under the License. module Quasar.Advanced.Paths where -import Data.Path.Pathy (RelDir, RelFile, Sandboxed, file, dir, ()) +import Pathy (RelDir, RelFile, dir, file, ()) +import Data.Symbol (SProxy(..)) -oidcProviders ∷ RelFile Sandboxed -oidcProviders = dir "security" dir "oidc" file "providers" +oidcProviders ∷ RelFile +oidcProviders = dir (SProxy ∷ SProxy "security") dir (SProxy ∷ SProxy "oidc") file (SProxy ∷ SProxy "providers") -token ∷ RelDir Sandboxed -token = dir "security" dir "token" +token ∷ RelDir +token = dir (SProxy ∷ SProxy "security") dir (SProxy ∷ SProxy "token") -group ∷ RelDir Sandboxed -group = dir "security" dir "group" +group ∷ RelDir +group = dir (SProxy ∷ SProxy "security") dir (SProxy ∷ SProxy "group") -permission ∷ RelDir Sandboxed -permission = dir "security" dir "permission" +permission ∷ RelDir +permission = dir (SProxy ∷ SProxy "security") dir (SProxy ∷ SProxy "permission") -authority ∷ RelDir Sandboxed -authority = dir "security" dir "authority" +children ∷ RelFile +children = file (SProxy ∷ SProxy "children") -licenseInfo ∷ RelFile Sandboxed -licenseInfo = dir "server" file "licenseInfo" +authority ∷ RelDir +authority = dir (SProxy ∷ SProxy "security") dir (SProxy ∷ SProxy "authority") -licensee ∷ RelFile Sandboxed -licensee = dir "server" file "licensee" +licenseInfo ∷ RelFile +licenseInfo = dir (SProxy ∷ SProxy "server") file (SProxy ∷ SProxy "licenseInfo") -pdfInfo ∷ RelFile Sandboxed -pdfInfo = dir "service" dir "pdf" file "info" +licensee ∷ RelFile +licensee = dir (SProxy ∷ SProxy "server") file (SProxy ∷ SProxy "licensee") -generatePdf ∷ RelFile Sandboxed -generatePdf = dir "service" dir "pdf" file "generate" +pdfInfo ∷ RelFile +pdfInfo = dir (SProxy ∷ SProxy "service") dir (SProxy ∷ SProxy "pdf") file (SProxy ∷ SProxy "info") + +generatePdf ∷ RelFile +generatePdf = dir (SProxy ∷ SProxy "service") dir (SProxy ∷ SProxy "pdf") file (SProxy ∷ SProxy "generate") diff --git a/src/Quasar/Advanced/QuasarAF.purs b/src/Quasar/Advanced/QuasarAF.purs index 7b14f68..464d214 100644 --- a/src/Quasar/Advanced/QuasarAF.purs +++ b/src/Quasar/Advanced/QuasarAF.purs @@ -30,6 +30,7 @@ import Data.Foldable (class Foldable, foldMap) import Data.Functor.Coproduct (Coproduct, left, right) import Data.Maybe (Maybe(..)) import Data.Time.Duration (Seconds) +import Pathy (AbsDir, AbsFile, AbsPath) import Quasar.Advanced.Types as QA import Quasar.Data (QData) import Quasar.Data.Json (PrecisionMode(..)) @@ -42,7 +43,7 @@ import Quasar.Mount.View as View import Quasar.QuasarF (QuasarF(..)) import Quasar.Query.OutputMeta (OutputMeta) import Quasar.ServerInfo (ServerInfo) -import Quasar.Types (AnyPath, FilePath, DirPath, Pagination, Vars, CompileResultR) +import Quasar.Types (Pagination, Vars, CompileResultR) import SqlSquared (SqlQuery) data QuasarAF a @@ -81,7 +82,7 @@ serverInfo = readQuery ∷ PrecisionMode - → DirPath + → AbsDir → SqlQuery → Vars → Maybe Pagination @@ -90,7 +91,7 @@ readQuery mode path sql vars pagination = left $ ReadQuery mode path sql vars pagination id readQueryEJson - ∷ DirPath + ∷ AbsDir → SqlQuery → Vars → Maybe Pagination @@ -99,8 +100,8 @@ readQueryEJson path sql vars pagination = readQuery Precise path sql vars pagination <#> resultsAsEJson writeQuery - ∷ DirPath - → FilePath + ∷ AbsDir + → AbsFile → SqlQuery → Vars → QuasarAFCE OutputMeta @@ -108,7 +109,7 @@ writeQuery path file sql vars = left $ WriteQuery path file sql vars id compileQuery - ∷ DirPath + ∷ AbsDir → SqlQuery → Vars → QuasarAFCE CompileResultR @@ -116,13 +117,13 @@ compileQuery path sql vars = left $ CompileQuery path sql vars id fileMetadata - ∷ FilePath + ∷ AbsFile → QuasarAFCE Unit fileMetadata path = left $ FileMetadata path id dirMetadata - ∷ DirPath + ∷ AbsDir → Maybe Pagination → QuasarAFCE (Array QResource) dirMetadata path pagination = @@ -130,75 +131,75 @@ dirMetadata path pagination = readFile ∷ PrecisionMode - → FilePath + → AbsFile → Maybe Pagination → QuasarAFCE JArray readFile mode path pagination = left $ ReadFile mode path pagination id readFileEJson - ∷ FilePath + ∷ AbsFile → Maybe Pagination → QuasarAFCE (Array EJson) readFileEJson path pagination = readFile Precise path pagination <#> resultsAsEJson writeFile - ∷ FilePath + ∷ AbsFile → QData → QuasarAFCE Unit writeFile path content = left $ WriteFile path content id writeDir - ∷ DirPath + ∷ AbsDir → Blob → QuasarAFCE Unit writeDir path content = left $ WriteDir path content id appendFile - ∷ FilePath + ∷ AbsFile → QData → QuasarAFCE Unit appendFile path content = left $ AppendFile path content id deleteData - ∷ AnyPath + ∷ AbsPath → QuasarAFCE Unit deleteData path = left $ DeleteData path id moveData - ∷ AnyPath - → AnyPath + ∷ AbsPath + → AbsPath → QuasarAFCE Unit moveData from to = left $ MoveData from to id getMount - ∷ AnyPath + ∷ AbsPath → QuasarAFCE MountConfig getMount path = left $ GetMount path id createMount - ∷ AnyPath + ∷ AbsPath → MountConfig → QuasarAFCE Unit createMount path config = left $ CreateMount path config Nothing id updateMount - ∷ AnyPath + ∷ AbsPath → MountConfig → QuasarAFCE Unit updateMount path config = left $ UpdateMount path config Nothing id createCachedView - ∷ AnyPath + ∷ AbsPath → View.Config → Seconds → QuasarAFCE Unit @@ -206,7 +207,7 @@ createCachedView path config maxAge = left $ CreateMount path (ViewConfig config) (Just maxAge) id updateCachedView - ∷ AnyPath + ∷ AbsPath → View.Config → Seconds → QuasarAFCE Unit @@ -214,14 +215,14 @@ updateCachedView path config maxAge = left $ UpdateMount path (ViewConfig config) (Just maxAge) id moveMount - ∷ AnyPath - → AnyPath + ∷ AbsPath + → AbsPath → QuasarAFCE Unit moveMount from to = left $ MoveMount from to id deleteMount - ∷ AnyPath + ∷ AbsPath → QuasarAFCE Unit deleteMount path = left $ DeleteMount path id diff --git a/src/Quasar/Advanced/QuasarAF/Interpreter/Affjax.purs b/src/Quasar/Advanced/QuasarAF/Interpreter/Affjax.purs index 12de09f..a75deca 100644 --- a/src/Quasar/Advanced/QuasarAF/Interpreter/Affjax.purs +++ b/src/Quasar/Advanced/QuasarAF/Interpreter/Affjax.purs @@ -32,19 +32,17 @@ import Data.Either (Either(..)) import Data.Foldable (foldMap) import Data.Functor.Coproduct (Coproduct, left, right, coproduct) import Data.HTTP.Method (Method(..)) -import Data.List as List import Data.Maybe (Maybe(..), maybe) import Data.Monoid (mempty) -import Data.Path.Pathy (()) -import Data.Path.Pathy as Pt import Data.String as Str import Data.Tuple (Tuple(..), snd) -import Data.URI as URI import Network.HTTP.Affjax as AX import Network.HTTP.Affjax.Request (RequestContent, toRequest) import Network.HTTP.AffjaxF as AXF import Network.HTTP.RequestHeader as Req import OIDC.Crypt.Types as OIDC +import Pathy (Name(..), ()) +import Pathy as Pt import Quasar.Advanced.Paths as Paths import Quasar.Advanced.QuasarAF (QuasarAFC, QuasarAF(..)) import Quasar.Advanced.QuasarAF.Interpreter.Config (Config) @@ -54,6 +52,7 @@ import Quasar.ConfigF as CF import Quasar.Error (QResponse) import Quasar.QuasarF.Interpreter.Affjax as QCI import Quasar.QuasarF.Interpreter.Internal (ask, defaultRequest, jsonResult, mkRequest, mkUrl, unitResult) +import Quasar.URI as URI type M r = Free (Coproduct (CF.ConfigF (Config r)) (AXF.AffjaxFP RequestContent String)) @@ -120,15 +119,15 @@ evalQuasarAdvanced (AuthorityList k) = do evalQuasarAdvanced (PermissionInfo pid k) = do config ← ask url ← mkUrl - (Right (Paths.permission Pt.file (Qa.runPermissionId pid))) - (URI.Query (List.singleton (Tuple "transitive" Nothing))) + (Right (Paths.permission Pt.file' (Name $ Qa.runPermissionId pid))) + (URI.QueryPairs [Tuple "transitive" Nothing]) map k $ mkAuthedRequest (jsonResult >>> map Qa.runPermission) $ _{ url = url } evalQuasarAdvanced (PermissionChildren pid isTransitive k) = do config ← ask url ← mkUrl - (Right (Paths.permission Pt.dir (Qa.runPermissionId pid) Pt.file "children")) + (Right (Paths.permission Pt.dir' (Name $ Qa.runPermissionId pid) Paths.children )) (transitiveQuery isTransitive) map k $ mkAuthedRequest (jsonResult >>> map (map Qa.runPermission)) @@ -145,7 +144,7 @@ evalQuasarAdvanced (SharePermission req k) = do evalQuasarAdvanced (DeletePermission pid k) = do config ← ask url ← mkUrl - (Right (Paths.permission Pt.file (Qa.runPermissionId pid))) + (Right (Paths.permission Pt.file' (Name $ Qa.runPermissionId pid))) mempty map k $ mkAuthedRequest unitResult @@ -161,7 +160,7 @@ evalQuasarAdvanced (TokenList k) = do evalQuasarAdvanced (TokenInfo tid k) = do config ← ask url ← mkUrl - (Right (Paths.token Pt.file (Qa.runTokenId tid))) + (Right (Paths.token Pt.file' (Name $ Qa.runTokenId tid))) mempty map k $ mkAuthedRequest (jsonResult >>> map Qa.runToken) @@ -181,7 +180,7 @@ evalQuasarAdvanced (CreateToken mbName actions k) = do } evalQuasarAdvanced (UpdateToken tid actions k) = do config ← ask - url ← mkUrl (Right (Paths.token Pt.file (Qa.runTokenId tid))) mempty + url ← mkUrl (Right (Paths.token Pt.file' (Name $ Qa.runTokenId tid))) mempty map k $ mkAuthedRequest (jsonResult >>> map Qa.runToken) $ _{ url = url @@ -194,7 +193,7 @@ evalQuasarAdvanced (UpdateToken tid actions k) = do evalQuasarAdvanced (DeleteToken tid k) = do config ← ask url ← mkUrl - (Right (Paths.token Pt.file (Qa.runTokenId tid))) + (Right (Paths.token Pt.file' (Name $ Qa.runTokenId tid))) mempty map k $ mkAuthedRequest unitResult @@ -226,9 +225,9 @@ evalQuasarAdvanced (PDFInfo k) = do $ mkAuthedRequest (const (Right unit)) $ _{ url = url } -transitiveQuery ∷ Boolean → URI.Query +transitiveQuery ∷ Boolean → URI.QQuery transitiveQuery b = - if b then URI.Query (List.singleton (Tuple "transitive" Nothing)) else mempty + if b then URI.QueryPairs [Tuple "transitive" Nothing] else mempty mkAuthedRequest ∷ ∀ a r diff --git a/src/Quasar/Advanced/QuasarAF/Interpreter/Internal.purs b/src/Quasar/Advanced/QuasarAF/Interpreter/Internal.purs index 9f43fd2..a8d672d 100644 --- a/src/Quasar/Advanced/QuasarAF/Interpreter/Internal.purs +++ b/src/Quasar/Advanced/QuasarAF/Interpreter/Internal.purs @@ -22,23 +22,23 @@ import Control.Monad.Free (Free) import Data.Either (Either(..)) import Data.Functor.Coproduct (Coproduct) import Data.Maybe (Maybe(..)) -import Data.Path.Pathy (rootDir) import Data.String as String -import Data.URI as URI import Network.HTTP.Affjax.Request (RequestContent) import Network.HTTP.AffjaxF as AXF +import Pathy (rootDir) import Quasar.Advanced.Paths as Paths import Quasar.Advanced.QuasarAF (GroupPath(..)) import Quasar.Advanced.QuasarAF.Interpreter.Config (Config) import Quasar.ConfigF as CF import Quasar.QuasarF.Interpreter.Internal (mkFSUrl) +import Quasar.URI as URI type AXFP = AXF.AffjaxFP RequestContent String mkGroupUrl ∷ ∀ r . GroupPath - → URI.Query + → URI.QQuery → Free (Coproduct (CF.ConfigF (Config r)) AXFP) String mkGroupUrl (GroupPath gp) q = do url ← mkFSUrl Paths.group (Left gp) q diff --git a/src/Quasar/Advanced/Types.purs b/src/Quasar/Advanced/Types.purs index 7823d06..9f38ca3 100644 --- a/src/Quasar/Advanced/Types.purs +++ b/src/Quasar/Advanced/Types.purs @@ -5,35 +5,43 @@ import Prelude import Control.Alt ((<|>)) import Data.Argonaut (class EncodeJson, class DecodeJson, encodeJson, decodeJson, Json, JString, (.?), (:=), (~>), jsonEmptyObject) import Data.Bifunctor (lmap) -import Data.Either (Either(..)) -import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) +import Data.Either (Either(..), note) +import Data.Maybe (Maybe(..), fromMaybe, isJust) import Data.Newtype as Newtype -import Data.Path.Pathy (()) -import Data.Path.Pathy as Pt import Data.String as Str +import Data.String.NonEmpty (NonEmptyString) +import Data.String.NonEmpty as NES import Data.Traversable (traverse) import OIDC.Crypt.JSONWebKey (JSONWebKey) import OIDC.Crypt.Types (Issuer(..), ClientId(..)) +import Partial.Unsafe (unsafePartial) +import Pathy (AbsDir, AbsFile, rootDir) +import Quasar.Types (parseQDirPath, parseQFilePath, printQPath) -newtype GroupPath = GroupPath (Pt.AbsDir Pt.Sandboxed) +newtype GroupPath = GroupPath AbsDir derive instance eqGroupPath ∷ Eq GroupPath derive instance ordGroupPath ∷ Ord GroupPath derive instance newtypeGroupPath ∷ Newtype.Newtype GroupPath _ printGroupPath ∷ GroupPath → String -printGroupPath gp = +printGroupPath = NES.toString <<< runGroupPath + +runGroupPath ∷ GroupPath → NonEmptyString +runGroupPath gp = let dir = Newtype.un GroupPath gp in - -- TODO(Christoph): Get rid of this once quasar treats Groups as directories - if dir == Pt.rootDir - then Pt.printPath dir - else fromMaybe "/" (Str.stripSuffix (Str.Pattern "/") (Pt.printPath dir)) + + unsafePartial $ NES.unsafeFromString + -- TODO(Christoph): Get rid of this once quasar treats Groups as directories + if dir == rootDir + then printQPath dir + else fromMaybe "/" (Str.stripSuffix (Str.Pattern "/") (printQPath dir)) parseGroupPath ∷ String → Either String GroupPath -- TODO(Christoph): Clean this up once Quasar treats Groups as directories -parseGroupPath s = map GroupPath if s == "/" then Right Pt.rootDir else parseDir (s <> "/") +parseGroupPath s = map GroupPath if s == "/" then Right rootDir else parseDir (s <> "/") data Operation = Add @@ -58,7 +66,7 @@ instance decodeJsonOperation ∷ DecodeJson Operation where "Read" → pure Read "Modify" → pure Modify "Delete" → pure Delete - _ → Left "Incorrect permission" + _ → Left "Could not parse permission" data AccessType @@ -79,20 +87,20 @@ instance decodeJsonAccessType ∷ DecodeJson AccessType where "Structural" → pure Structural "Content" → pure Content "Mount" → pure Mount - _ → Left "Incorrect resource type" + _ → Left "Could not parse resource type" data QResource - = File (Pt.AbsFile Pt.Sandboxed) - | Dir (Pt.AbsDir Pt.Sandboxed) + = File AbsFile + | Dir AbsDir | Group GroupPath derive instance eqQResource ∷ Eq QResource derive instance ordQResource ∷ Ord QResource instance encodeJsonQResource ∷ EncodeJson QResource where - encodeJson (File pt) = encodeJson $ "data:" <> Pt.printPath pt - encodeJson (Dir pt) = encodeJson $ "data:" <> Pt.printPath pt + encodeJson (File pt) = encodeJson $ "data:" <> printQPath pt + encodeJson (Dir pt) = encodeJson $ "data:" <> printQPath pt encodeJson (Group gpt) = encodeJson $ "group:" <> printGroupPath gpt instance decodeJsonQResource ∷ DecodeJson QResource where @@ -102,29 +110,21 @@ instance decodeJsonQResource ∷ DecodeJson QResource where groupPath = Str.stripPrefix (Str.Pattern "group:") str filePath = Str.stripPrefix (Str.Pattern "data:") str case groupPath, filePath of - Nothing, Nothing → Left "Incorrect resource" + Nothing, Nothing → Left "Could not parse resource" Just pt, _ → map Group - $ lmap (const "Incorrect group resource") + $ lmap (const "Could not parse group resource") $ parseGroupPath pt _, Just pt → - (map File $ lmap (const $ "Incorrect file resource") $ parseFile pt) + (map File $ lmap (const $ "Could not parse file resource") $ parseFile pt) <|> - (map Dir $ lmap (const $ "Incorrect directory resource") $ parseDir pt) + (map Dir $ lmap (const $ "Could not parse directory resource") $ parseDir pt) -parseFile ∷ String → Either String (Pt.AbsFile Pt.Sandboxed) -parseFile pt = - Pt.parseAbsFile pt - >>= Pt.sandbox Pt.rootDir - <#> (Pt.rootDir _) - # maybe (Left "Incorrect resource") pure +parseFile ∷ String → Either String AbsFile +parseFile = parseQFilePath >>> note "Could not parse resource" -parseDir ∷ String → Either String (Pt.AbsDir Pt.Sandboxed) -parseDir pt = - Pt.parseAbsDir pt - >>= Pt.sandbox Pt.rootDir - <#> (Pt.rootDir _) - # maybe (Left "Incorrect resource") pure +parseDir ∷ String → Either String AbsDir +parseDir = parseQDirPath >>> note "Could not parse resource" type ActionR = @@ -159,47 +159,59 @@ instance decodeJsonAction ∷ DecodeJson Action where <*> (obj .? "accessType") <#> Action -newtype UserId = UserId String +newtype UserId = UserId NonEmptyString -runUserId ∷ UserId → String +runUserId ∷ UserId → NonEmptyString runUserId (UserId s) = s +printUserId ∷ UserId → String +printUserId = NES.toString <<< runUserId + derive instance eqUserId ∷ Eq UserId derive instance ordUserId ∷ Ord UserId instance encodeJsonUserId ∷ EncodeJson UserId where - encodeJson = encodeJson <<< runUserId + encodeJson = encodeNEString <<< runUserId instance decodeJsonUserId ∷ DecodeJson UserId where - decodeJson = map UserId <<< decodeJson + decodeJson = map UserId <<< decodeNEString + +encodeNEString ∷ NonEmptyString → Json +encodeNEString = encodeJson <<< NES.toString +decodeNEString ∷ Json → Either String NonEmptyString +decodeNEString j = do + str ← decodeJson j + case NES.fromString str of + Nothing → Left "Expected string to be non empty" + Just a → pure a -newtype TokenId = TokenId String +newtype TokenId = TokenId NonEmptyString -runTokenId ∷ TokenId → String +runTokenId ∷ TokenId → NonEmptyString runTokenId (TokenId s) = s derive instance eqTokenId ∷ Eq TokenId derive instance ordTokenId ∷ Ord TokenId instance encodeJsonTokenId ∷ EncodeJson TokenId where - encodeJson = runTokenId >>> encodeJson + encodeJson = encodeNEString <<< runTokenId instance decodeJsonTokenId ∷ DecodeJson TokenId where - decodeJson = map TokenId <<< decodeJson + decodeJson = map TokenId <<< decodeNEString -newtype PermissionId = PermissionId String -runPermissionId ∷ PermissionId → String +newtype PermissionId = PermissionId NonEmptyString +runPermissionId ∷ PermissionId → NonEmptyString runPermissionId (PermissionId s) = s derive instance eqPermissionId ∷ Eq PermissionId derive instance ordPermissionId ∷ Ord PermissionId instance encodeJsonPermissionId ∷ EncodeJson PermissionId where - encodeJson = encodeJson <<< runPermissionId + encodeJson = encodeNEString <<< runPermissionId instance decodeJsonPermissionId ∷ DecodeJson PermissionId where - decodeJson = map PermissionId <<< decodeJson + decodeJson = map PermissionId <<< decodeNEString data GrantedTo @@ -230,20 +242,28 @@ instance decodeJsonGrantedTo ∷ DecodeJson GrantedTo where -- string isn't email. if isJust (Str.indexOf (Str.Pattern "@") str) then pure str - else Left "Incorrect email" + else Left "Could not parse email" parseUserId ∷ String → Either String UserId parseUserId str = - Str.stripPrefix (Str.Pattern "user:") str # maybe (Left "Incorrect user") (pure <<< UserId) + Str.stripPrefix (Str.Pattern "user:") str + >>= NES.fromString + # map UserId + # note "Could not parse user" + parseTokenId ∷ String → Either String TokenId parseTokenId str = - Str.stripPrefix (Str.Pattern "token:") str # maybe (Left "Incorrect token") (pure <<< TokenId) + Str.stripPrefix (Str.Pattern "token:") str + >>= NES.fromString + # map TokenId + # note "Could not parse token" + parseGroup ∷ String → Either String GroupPath parseGroup string = Str.stripPrefix (Str.Pattern "group:") string - # maybe (Left "Incorrect group") pure + # note "Could not parse group" >>= parseGroupPath @@ -294,14 +314,10 @@ instance decodeJsonGroupInfo ∷ DecodeJson GroupInfo where extractGroups ∷ Array String → Either String (Array GroupPath) extractGroups = traverse \x → - note "Incorrect subgroup" do + note "Could not parse subgroup" do -- Quasar returns file paths for the subgroups, so we have to append a slash - dir ← Pt.parseAbsDir (x <> "/") - sandboxed ← Pt.sandbox Pt.rootDir dir - pure $ GroupPath $ Pt.rootDir sandboxed - -note :: ∀ a b. a → Maybe b → Either a b -note n m = maybe (Left n) Right m + dir ← parseQDirPath (x <> "/") + pure $ GroupPath dir type GroupPatchR = @@ -325,7 +341,7 @@ data ShareableSubject instance encodeJsonShareableSubject ∷ EncodeJson ShareableSubject where encodeJson (UserSubject (UserId uid)) = - encodeJson $ "user:" <> uid + encodeJson $ "user:" <> NES.toString uid encodeJson (GroupSubject gpt) = encodeJson $ printGroupPath gpt @@ -343,7 +359,7 @@ runShareRequest (ShareRequest r) = r instance encodeJsonShareRequest ∷ EncodeJson ShareRequest where encodeJson (ShareRequest obj) = - "subjects" := ((map (append "user:" <<< runUserId) obj.users) + "subjects" := ((map (append "user:" <<< printUserId) obj.users) <> map (append "group:" <<< printGroupPath) obj.groups) ~> "actions" := (map Action $ obj.actions) ~> jsonEmptyObject diff --git a/src/Quasar/FS/DirMetadata.purs b/src/Quasar/FS/DirMetadata.purs index 0661420..c6835e4 100644 --- a/src/Quasar/FS/DirMetadata.purs +++ b/src/Quasar/FS/DirMetadata.purs @@ -24,14 +24,13 @@ import Prelude import Data.Argonaut (Json, decodeJson, (.?)) import Data.Either (Either) import Data.Traversable (traverse) - +import Pathy (AbsDir) import Quasar.FS.Resource (QResource) import Quasar.FS.Resource as QResource -import Quasar.Types (DirPath) type DirMetadata = Array QResource -fromJSON ∷ DirPath → Json → Either String DirMetadata +fromJSON ∷ AbsDir → Json → Either String DirMetadata fromJSON parent json = do obj ← decodeJson json children ← obj .? "children" diff --git a/src/Quasar/FS/Mount.purs b/src/Quasar/FS/Mount.purs index 44c8da5..8d3c326 100644 --- a/src/Quasar/FS/Mount.purs +++ b/src/Quasar/FS/Mount.purs @@ -20,27 +20,29 @@ import Prelude import Control.Alt ((<|>)) import Data.Argonaut (Json, decodeJson, (.?)) +import Data.Bifunctor (bimap) import Data.Const (Const(..)) -import Data.Either (Either(..)) +import Data.Either (Either(..), note) import Data.Eq (class Eq1, eq1) import Data.Identity (Identity(..)) import Data.Maybe (Maybe) import Data.Newtype (unwrap) import Data.Ord (class Ord1, compare1) -import Data.Path.Pathy (DirName, FileName, dir, file, pathName, ()) +import Data.String.NonEmpty (fromString) import Data.TacitString as TS -import Quasar.Types (AnyPath, DirPath, FilePath) +import Pathy (AbsDir, AbsFile, Dir, File, Name(..), AbsPath, dir', file', fileName, name, ()) + data MountF f - = View (f FilePath) - | Module (f DirPath) - | MongoDB (f DirPath) - | Couchbase (f DirPath) - | MarkLogic (f DirPath) - | SparkHDFS (f DirPath) - | SparkLocal (f DirPath) - | Mimir (f DirPath) - | Unknown String (f AnyPath) + = View (f AbsFile) + | Module (f AbsDir) + | MongoDB (f AbsDir) + | Couchbase (f AbsDir) + | MarkLogic (f AbsDir) + | SparkHDFS (f AbsDir) + | SparkLocal (f AbsDir) + | Mimir (f AbsDir) + | Unknown String (f AbsPath) type Mount = MountF Identity type MountType = MountF (Const Unit) @@ -97,7 +99,7 @@ instance ordMount ∷ Ord1 f ⇒ Ord (MountF f) where instance showMount ∷ (Show (f TS.TacitString), Functor f) ⇒ Show (MountF f) where show = let - show' :: forall a. Show a ⇒ f a → String + show' ∷ ∀ a. Show a ⇒ f a → String show' = map (show >>> TS.hush) >>> show in case _ of View p → "(View " <> show' p <> ")" @@ -112,19 +114,19 @@ instance showMount ∷ (Show (f TS.TacitString), Functor f) ⇒ Show (MountF f) -- | Attempts to decode a mount listing value from Quasar's filesystem metadata, -- | for a mount in the specified parent directory. -fromJSON ∷ DirPath → Json → Either String Mount +fromJSON ∷ AbsDir → Json → Either String Mount fromJSON parent = decodeJson >=> \obj → do mount ← obj .? "mount" typ ← obj .? "type" - name ← obj .? "name" + name' ← note "empty name" <<< fromString =<< (obj .? "name") let - err :: forall a. Either String a + err ∷ ∀ a. Either String a err = Left $ "Unexpected type '" <> typ <> "' for mount '" <> mount <> "'" - onFile :: Either String (Identity FilePath) - onFile = if typ == "file" then Right $ Identity $ parent file name else err - onDir :: Either String (Identity DirPath) - onDir = if typ == "directory" then Right $ Identity $ parent dir name else err - onAnyPath :: Either String (Identity AnyPath) + onFile ∷ Either String (Identity AbsFile) + onFile = if typ == "file" then Right $ Identity $ parent file' (Name name') else err + onDir ∷ Either String (Identity AbsDir) + onDir = if typ == "directory" then Right $ Identity $ parent dir' (Name name') else err + onAnyPath ∷ Either String (Identity AbsPath) onAnyPath = map (map Left) onDir <|> map (map Right) onFile case typeFromName mount of View _ → View <$> onFile @@ -137,14 +139,14 @@ fromJSON parent = decodeJson >=> \obj → do Mimir _ → Mimir <$> onDir Unknown n _ → Unknown n <$> onAnyPath -foldPath ∷ ∀ r. (DirPath → r) → (FilePath → r) → Mount → r +foldPath ∷ ∀ r. (AbsDir → r) → (AbsFile → r) → Mount → r foldPath onDir onPath = overPath (onDir >>> Const) (onPath >>> Const) >>> unwrap -getPath ∷ Mount → AnyPath +getPath ∷ Mount → AbsPath getPath = foldPath Left Right -getName ∷ Mount → Either (Maybe DirName) FileName -getName = getPath >>> pathName +getName ∷ Mount → Either (Maybe (Name Dir)) (Name File) +getName = getPath >>> bimap name fileName typeFromName ∷ String → MountType typeFromName = case _ of @@ -158,7 +160,7 @@ typeFromName = case _ of "mimir" → Mimir $ Const unit other → Unknown other $ Const unit -overPath ∷ ∀ f. Functor f ⇒ (DirPath → f DirPath) → (FilePath → f FilePath) → Mount → f Mount +overPath ∷ ∀ f. Functor f ⇒ (AbsDir → f AbsDir) → (AbsFile → f AbsFile) → Mount → f Mount overPath overDir overFile = case _ of View (Identity file) → overFile file <#> Identity >>> View Module (Identity dir) → overDir dir <#> Identity >>> Module diff --git a/src/Quasar/FS/Mount/Gen.purs b/src/Quasar/FS/Mount/Gen.purs index e5a6bde..7604067 100644 --- a/src/Quasar/FS/Mount/Gen.purs +++ b/src/Quasar/FS/Mount/Gen.purs @@ -19,7 +19,6 @@ module Quasar.FS.Mount.Gen where import Prelude -import Quasar.FS.Mount (MountF(..), Mount, MountType) import Control.Monad.Gen (class MonadGen) import Control.Monad.Gen as Gen import Control.Monad.Rec.Class (class MonadRec) @@ -27,10 +26,12 @@ import Data.Const (Const(..)) import Data.Identity (Identity(..)) import Data.NonEmpty ((:|)) import Data.String.Gen (genUnicodeString) -import Quasar.Mount.Common.Gen (genAnyPath, genAbsDirPath, genAbsFilePath) +import Pathy.Gen (genAbsAnyPath) +import Quasar.FS.Mount (MountF(..), Mount, MountType) +import Quasar.Mount.Common.Gen (genAbsDirPath, genAbsFilePath) -genMountType :: ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MountType +genMountType ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MountType genMountType = Gen.oneOf $ (pure $ View $ Const unit) :| [ pure $ Module $ Const unit @@ -43,7 +44,7 @@ genMountType = Gen.oneOf , genUnicodeString <#> (_ `Unknown` Const unit) ] -genMount :: ∀ m. MonadGen m ⇒ MonadRec m ⇒ m Mount +genMount ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m Mount genMount = Gen.oneOf $ (genAbsFilePath <#> Identity >>> View) :| [ genAbsDirPath <#> Identity >>> Module @@ -53,5 +54,5 @@ genMount = Gen.oneOf , genAbsDirPath <#> Identity >>> SparkHDFS , genAbsDirPath <#> Identity >>> SparkLocal , genAbsDirPath <#> Identity >>> Mimir - , Unknown <$> genUnicodeString <*> map Identity genAnyPath + , Unknown <$> genUnicodeString <*> map Identity genAbsAnyPath ] diff --git a/src/Quasar/FS/Resource.purs b/src/Quasar/FS/Resource.purs index 79bd793..8fd2ce1 100644 --- a/src/Quasar/FS/Resource.purs +++ b/src/Quasar/FS/Resource.purs @@ -20,15 +20,16 @@ import Prelude import Control.Alt ((<|>)) import Data.Argonaut (Json, decodeJson, (.?)) -import Data.Either (Either(..)) +import Data.Bifunctor (bimap) +import Data.Either (Either(..), note) import Data.Maybe (Maybe) -import Data.Path.Pathy (DirName, FileName, dir, file, pathName, ()) +import Data.String.NonEmpty (fromString) +import Pathy (AbsDir, AbsFile, Dir, File, Name(..), AbsPath, dir', file', fileName, name, ()) import Quasar.FS.Mount as Mount -import Quasar.Types (AnyPath, FilePath, DirPath) data QResource - = File FilePath - | Directory DirPath + = File AbsFile + | Directory AbsDir | Mount Mount.Mount derive instance eqQResource ∷ Eq QResource @@ -39,21 +40,21 @@ instance showQResource ∷ Show QResource where show (Directory p) = "(Directory " <> show p <> ")" show (Mount m) = "(Mount " <> show m <> ")" -fromJSON ∷ DirPath → Json → Either String QResource +fromJSON ∷ AbsDir → Json → Either String QResource fromJSON parent json = Mount <$> Mount.fromJSON parent json <|> do obj ← decodeJson json - name ← obj .? "name" + name' ← note "empty name" <<< fromString =<< (obj .? "name") obj .? "type" >>= case _ of - "directory" → Right $ Directory (parent dir name) - "file" → Right $ File (parent file name) + "directory" → Right $ Directory (parent dir' (Name name')) + "file" → Right $ File (parent file' (Name name')) typ → Left $ "unknown resource type " <> typ -getPath ∷ QResource → AnyPath +getPath ∷ QResource → AbsPath getPath (File p) = Right p getPath (Directory p) = Left p getPath (Mount m) = Mount.getPath m -getName ∷ QResource → Either (Maybe DirName) FileName -getName = pathName <<< getPath +getName ∷ QResource → Either (Maybe (Name Dir)) (Name File) +getName = bimap name fileName <<< getPath diff --git a/src/Quasar/FS/Resource/Gen.purs b/src/Quasar/FS/Resource/Gen.purs index 60534f5..60472f1 100644 --- a/src/Quasar/FS/Resource/Gen.purs +++ b/src/Quasar/FS/Resource/Gen.purs @@ -29,9 +29,9 @@ import Quasar.FS.Resource (QResource(..)) import Quasar.Mount.Common.Gen (genAbsDirPath, genAbsFilePath) -genQResource :: ∀ m. MonadGen m ⇒ MonadRec m ⇒ m QResource +genQResource ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m QResource genQResource = Gen.frequency $ (Tuple 1.0 $ genAbsFilePath <#> File ) :| - [ Tuple 1.0 $ genAbsDirPath <#> Directory + [ Tuple 1.0 $ genAbsDirPath <#> Directory , Tuple 3.0 $ genMount <#> Mount ] diff --git a/src/Quasar/Mount/Common.purs b/src/Quasar/Mount/Common.purs deleted file mode 100644 index f22fc4e..0000000 --- a/src/Quasar/Mount/Common.purs +++ /dev/null @@ -1,65 +0,0 @@ -{- -Copyright 2017 SlamData, Inc. - -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 Quasar.Mount.Common where - -import Prelude - -import Data.Either (Either(..)) -import Data.Maybe (Maybe(..)) -import Data.Newtype (class Newtype) -import Data.String as Str -import Data.Tuple (Tuple) -import Data.URI as URI - -type Host = Tuple URI.Host (Maybe URI.Port) - -extractHost ∷ Maybe URI.Authority → Either String Host -extractHost (Just (URI.Authority _ hs)) = - case hs of - [h] → Right h - [] → Left "No host specified" - _ → Left "Multiple hosts specified" -extractHost _ = Left "No host specified" - -newtype Credentials = Credentials { user ∷ String, password ∷ String } - -derive instance newtypeCredentials ∷ Newtype Credentials _ -derive instance eqCredentials ∷ Eq Credentials -derive instance ordCredentials ∷ Ord Credentials - -instance showCredentials ∷ Show Credentials where - show (Credentials { user, password }) = - "(Credentials { user: " <> show user <> ", password: " <> show password <> " })" - -combineCredentials ∷ Credentials → URI.UserInfo -combineCredentials (Credentials { user, password }) - | Str.null password = URI.UserInfo user - | otherwise = URI.UserInfo (user <> ":" <> password) - -extractCredentials ∷ Maybe URI.Authority → Maybe Credentials -extractCredentials auth = do - URI.UserInfo userInfo ← (\(URI.Authority mui _) → mui) =<< auth - pure $ Credentials $ - case Str.indexOf (Str.Pattern ":") userInfo of - Nothing → - { user: userInfo - , password: "" - } - Just ix → - { user: Str.take ix userInfo - , password: Str.drop (ix + 1) userInfo - } diff --git a/src/Quasar/Mount/Common/Gen.purs b/src/Quasar/Mount/Common/Gen.purs index 8407bbe..61c3cc6 100644 --- a/src/Quasar/Mount/Common/Gen.purs +++ b/src/Quasar/Mount/Common/Gen.purs @@ -21,50 +21,45 @@ module Quasar.Mount.Common.Gen import Prelude -import Control.Monad.Gen (class MonadGen) +import Control.Monad.Gen (class MonadGen, filtered) import Control.Monad.Gen as Gen -import Control.Monad.Gen.Common as GenC +import Control.Monad.Gen.Common (genMaybe) import Control.Monad.Rec.Class (class MonadRec) import Data.Char.Gen as CG -import Data.Either (Either(..)) +import Data.Maybe (Maybe(..)) import Data.NonEmpty ((:|)) -import Data.Path.Pathy.Gen (genAbsDirPath, genAbsFilePath) as PGen -import Data.String as S import Data.String.Gen as SG -import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Quasar.Mount.Common (Credentials(..)) -import Quasar.Mount.MongoDB as MDB -import Quasar.Types (AnyPath) +import Data.String.NonEmpty (NonEmptyString) +import Data.String.NonEmpty as NES +import Data.These.Gen (genThese) +import Pathy.Gen (genAbsDirPath, genAbsFilePath) as PGen +import Quasar.URI as URI +import URI.Host.Gen as HostGen +import URI.Port as Port genAlphaNumericString ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m String -genAlphaNumericString = SG.genString $ Gen.oneOf $ CG.genDigitChar :| [CG.genAlpha] +genAlphaNumericString = SG.genString genAlphaNumericChar -genHostURI ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m URI.Host -genHostURI = Gen.oneOf $ genIPv4 :| [genName] - where - genIPv4 = do - a ← Gen.chooseInt 1 254 - b ← Gen.chooseInt 1 254 - c ← Gen.chooseInt 1 254 - d ← Gen.chooseInt 1 254 - pure $ URI.IPv4Address $ S.joinWith "." $ show <$> [a, b, c, d] - genName = do - head ← S.singleton <$> CG.genAlpha - tail ← genAlphaNumericString - pure $ URI.NameAddress $ head <> tail +genAlphaNumericNEString ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m NonEmptyString +genAlphaNumericNEString = NES.cons <$> genAlphaNumericChar <*> SG.genString genAlphaNumericChar -genPort ∷ ∀ m. MonadGen m ⇒ m URI.Port -genPort = URI.Port <$> Gen.chooseInt 50000 65535 +genAlphaNumericChar ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m Char +genAlphaNumericChar = Gen.oneOf $ CG.genDigitChar :| [CG.genAlpha] -genHost ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MDB.Host -genHost = Tuple <$> genHostURI <*> GenC.genMaybe genPort +genPort ∷ ∀ m. MonadRec m ⇒ MonadGen m ⇒ m URI.Port +genPort = filtered $ Port.fromInt <$> Gen.chooseInt 50000 65535 -genCredentials ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m Credentials -genCredentials = - Credentials <$> ({ user: _, password: _ } - <$> genAlphaNumericString - <*> Gen.choose (pure "") genAlphaNumericString) +genHost' ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m URI.QURIHost' +genHost' = genThese HostGen.genHost genPort + +genHost ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m URI.QURIHost +genHost = genMaybe genHost' -genAnyPath ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m AnyPath -genAnyPath = Gen.oneOf $ (Left <$> PGen.genAbsDirPath) :| [Right <$> PGen.genAbsFilePath] +genHosts ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m URI.QURIHosts +genHosts = Gen.unfoldable genHost' + +genCredentials ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m URI.UserPassInfo +genCredentials = + URI.UserPassInfo <$> ({ user: _, password: _ } + <$> genAlphaNumericNEString + <*> Gen.choose (pure Nothing) (Just <$> genAlphaNumericNEString)) diff --git a/src/Quasar/Mount/Couchbase.purs b/src/Quasar/Mount/Couchbase.purs index 715ed98..6780cf1 100644 --- a/src/Quasar/Mount/Couchbase.purs +++ b/src/Quasar/Mount/Couchbase.purs @@ -20,30 +20,28 @@ module Quasar.Mount.Couchbase , fromJSON , toURI , fromURI - , module Exports ) where import Prelude import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) import Data.Bifunctor (lmap) +import Data.Codec (decode, encode) import Data.Either (Either(..)) -import Data.List as L import Data.Maybe (Maybe(..), maybe, fromMaybe) import Data.Newtype (un) import Data.Number as Num -import Data.Path.Pathy (()) -import Data.Path.Pathy as P import Data.StrMap as SM +import Data.String.NonEmpty as NES import Data.Time.Duration (Seconds(..)) import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI -import Quasar.Mount.Common (Host) as Exports -import Quasar.Mount.Common (Host, extractHost) +import Pathy (Name(..), ()) +import Pathy as P +import Quasar.URI as URI +import URI.Scheme as Scheme type Config = - { host ∷ Host + { host ∷ URI.QURIHost' , bucketName ∷ String , password ∷ String , docTypeKey ∷ String @@ -52,43 +50,52 @@ type Config = toJSON ∷ Config → Json toJSON config = - let uri = AbsoluteURI.print (toURI config) + let uri = encode URI.qAbsoluteURI (toURI config) in "couchbase" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON = fromURI - <=< lmap show <<< AbsoluteURI.parse + <=< lmap show <<< decode URI.qAbsoluteURI <=< (_ .? "connectionUri") <=< (_ .? "couchbase") <=< decodeJson -toURI ∷ Config → URI.AbsoluteURI +toURI ∷ Config → URI.QAbsoluteURI toURI { host, bucketName, password, docTypeKey, queryTimeout } = URI.AbsoluteURI - (Just uriScheme) - (URI.HierarchicalPart - (Just (URI.Authority Nothing (pure host))) - (Just (Right (P.rootDir P.file bucketName)))) - (Just (URI.Query props)) + uriScheme + hierarchicalPart + (Just $ URI.QueryPairs props) where - props ∷ L.List (Tuple String (Maybe String)) - props = L.Nil - <> pure (Tuple "password" (Just password)) - <> pure (Tuple "docTypeKey" (Just docTypeKey)) - <> maybe L.Nil (pure <<< Tuple "queryTimeoutSeconds" <<< Just <<< show <<< un Seconds) queryTimeout + hierarchicalPart ∷ URI.QHierarchicalPart + hierarchicalPart = + URI.HierarchicalPartAuth + (URI.Authority Nothing $ Just host) + case NES.fromString bucketName of + Nothing → Just $ Left P.rootDir + Just n → Just $ Right $ P.rootDir P.file' (Name n) -fromURI ∷ URI.AbsoluteURI → Either String Config -fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth path) query) = do - unless (scheme == Just uriScheme) $ Left "Expected 'couchbase' URL scheme" - host ← extractHost auth + props ∷ Array (Tuple String (Maybe String)) + props = + [ Tuple "password" (Just password) + , Tuple "docTypeKey" (Just docTypeKey) + ] <> maybe [] (pure <<< Tuple "queryTimeoutSeconds" <<< Just <<< show <<< un Seconds) queryTimeout + +fromURI ∷ URI.QAbsoluteURI → Either String Config +fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPartNoAuth path) query) = + Left "Expected 'auth' part in URI" +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartAuth (URI.Authority _ Nothing) _) _) = do + Left "Expected 'host' part to be present in URL" +fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPartAuth (URI.Authority _ (Just host)) path) query) = do + unless (scheme == uriScheme) $ Left "Expected 'couchbase' URL scheme" bucketName ← case path of Nothing → Left "Path is missing from URL" Just (Left p) | p == P.rootDir → pure "" | otherwise → Left "Expected a file path" - Just (Right p) → pure $ P.runFileName $ P.fileName p - let props = maybe SM.empty (\(URI.Query qs) → SM.fromFoldable qs) query + Just (Right p) → pure $ NES.toString $ un P.Name $ P.fileName p + let props = maybe SM.empty (\(URI.QueryPairs qs) → SM.fromFoldable qs) query pure { host , bucketName @@ -98,4 +105,4 @@ fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth path) query) = do } uriScheme ∷ URI.Scheme -uriScheme = URI.Scheme "couchbase" +uriScheme = Scheme.unsafeFromString "couchbase" diff --git a/src/Quasar/Mount/Couchbase/Gen.purs b/src/Quasar/Mount/Couchbase/Gen.purs index 3141d78..458057d 100644 --- a/src/Quasar/Mount/Couchbase/Gen.purs +++ b/src/Quasar/Mount/Couchbase/Gen.purs @@ -19,18 +19,17 @@ module Quasar.Mount.Couchbase.Gen where import Prelude import Control.Monad.Gen (class MonadGen) -import Control.Monad.Gen as Gen import Control.Monad.Gen.Common as GenC import Control.Monad.Rec.Class (class MonadRec) import Data.Time.Duration.Gen (genSeconds) -import Quasar.Mount.Common.Gen (genAlphaNumericString, genHost) +import Quasar.Mount.Common.Gen (genAlphaNumericString, genHost') import Quasar.Mount.Couchbase as CB genConfig ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m CB.Config genConfig = { host: _, bucketName: _, password: _, docTypeKey: _, queryTimeout: _ } - <$> genHost - <*> Gen.choose (pure "") genAlphaNumericString + <$> genHost' + <*> genAlphaNumericString <*> genAlphaNumericString <*> genAlphaNumericString <*> GenC.genMaybe genSeconds diff --git a/src/Quasar/Mount/Gen.purs b/src/Quasar/Mount/Gen.purs index 2a86f06..b8fa851 100644 --- a/src/Quasar/Mount/Gen.purs +++ b/src/Quasar/Mount/Gen.purs @@ -33,7 +33,7 @@ import Quasar.Mount.Unknown.Gen as Unknown import Quasar.Mount.Module.Gen as Module import Quasar.Mount.View.Gen as View -genMountConfig :: ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MountConfig +genMountConfig ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MountConfig genMountConfig = Gen.oneOf $ (MongoDBConfig <$> MongoDB.genConfig) :| [ CouchbaseConfig <$> Couchbase.genConfig diff --git a/src/Quasar/Mount/MarkLogic.purs b/src/Quasar/Mount/MarkLogic.purs index 2c9215b..625f6d6 100644 --- a/src/Quasar/Mount/MarkLogic.purs +++ b/src/Quasar/Mount/MarkLogic.purs @@ -21,28 +21,25 @@ module Quasar.Mount.MarkLogic , fromJSON , toURI , fromURI - , module Exports ) where import Prelude import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) import Data.Bifunctor (lmap) +import Data.Codec (decode, encode) import Data.Either (Either(..)) -import Data.List as L import Data.Maybe (Maybe(..), maybe) import Data.StrMap as SM import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI -import Quasar.Mount.Common (Host, Credentials(..)) as Exports -import Quasar.Mount.Common (Host, Credentials, combineCredentials, extractCredentials, extractHost) -import Quasar.Types (AnyPath) +import Pathy (AbsPath) +import Quasar.URI as URI +import URI.Scheme as Scheme type Config = - { host ∷ Host - , path ∷ Maybe AnyPath - , credentials ∷ Maybe Credentials + { host ∷ URI.QURIHost' + , path ∷ Maybe AbsPath + , credentials ∷ Maybe URI.UserPassInfo , format ∷ Format } @@ -60,39 +57,38 @@ instance showFormat ∷ Show Format where toJSON ∷ Config → Json toJSON config = - let uri = AbsoluteURI.print (toURI config) + let uri = encode URI.qAbsoluteURI (toURI config) in "marklogic" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON = fromURI - <=< lmap show <<< AbsoluteURI.parse + <=< lmap show <<< decode URI.qAbsoluteURI <=< (_ .? "connectionUri") <=< (_ .? "marklogic") <=< decodeJson -toURI ∷ Config → URI.AbsoluteURI +toURI ∷ Config → URI.QAbsoluteURI toURI { host, path, credentials, format } = URI.AbsoluteURI - (Just uriScheme) - (URI.HierarchicalPart (Just (URI.Authority (combineCredentials <$> credentials) (pure host))) path) - (Just (URI.Query props)) + uriScheme + (URI.HierarchicalPartAuth (URI.Authority credentials (Just host)) path) + (Just (URI.QueryPairs [ (Tuple "format" (Just formatStr)) ])) where - props ∷ L.List (Tuple String (Maybe String)) - props = L.singleton (Tuple "format" (Just formatStr)) - formatStr ∷ String formatStr = case format of JSON → "json" XML → "xml" -fromURI ∷ URI.AbsoluteURI → Either String Config -fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth path) query) = do - unless (scheme == Just uriScheme) $ Left "Expected 'xcc' URL scheme" - host ← extractHost auth +fromURI ∷ URI.QAbsoluteURI → Either String Config +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartNoAuth _) _) = do + Left "Expected 'auth' part in URI" +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartAuth (URI.Authority _ Nothing) _) _) = do + Left "Expected 'host' part to be present in URL" +fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPartAuth (URI.Authority credentials (Just host)) path) query) = do + unless (scheme == uriScheme) $ Left "Expected 'xcc' URL scheme" let - credentials = extractCredentials auth - props = maybe SM.empty (\(URI.Query qs) → SM.fromFoldable qs) query + props = maybe SM.empty (\(URI.QueryPairs qs) → SM.fromFoldable qs) query format ← case join $ SM.lookup "format" props of Nothing → pure XML Just "xml" → pure XML @@ -101,4 +97,4 @@ fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth path) query) = do pure { host, path, credentials, format} uriScheme ∷ URI.Scheme -uriScheme = URI.Scheme "xcc" +uriScheme = Scheme.unsafeFromString "xcc" diff --git a/src/Quasar/Mount/MarkLogic/Gen.purs b/src/Quasar/Mount/MarkLogic/Gen.purs index 60d6eeb..1d43380 100644 --- a/src/Quasar/Mount/MarkLogic/Gen.purs +++ b/src/Quasar/Mount/MarkLogic/Gen.purs @@ -22,7 +22,8 @@ import Control.Monad.Gen (class MonadGen) import Control.Monad.Gen as Gen import Control.Monad.Gen.Common as GenC import Control.Monad.Rec.Class (class MonadRec) -import Quasar.Mount.Common.Gen (genAnyPath, genCredentials, genHost) +import Pathy.Gen (genAbsAnyPath) +import Quasar.Mount.Common.Gen (genCredentials, genHost') import Quasar.Mount.MarkLogic as ML genFormat ∷ ∀ m. MonadGen m ⇒ m ML.Format @@ -31,7 +32,7 @@ genFormat = Gen.choose (pure ML.JSON) (pure ML.XML) genConfig ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m ML.Config genConfig = { host: _, path: _, credentials: _, format: _ } - <$> genHost - <*> GenC.genMaybe genAnyPath + <$> genHost' + <*> GenC.genMaybe genAbsAnyPath <*> GenC.genMaybe genCredentials <*> genFormat diff --git a/src/Quasar/Mount/Mimir.purs b/src/Quasar/Mount/Mimir.purs index c81029a..7ae64b2 100644 --- a/src/Quasar/Mount/Mimir.purs +++ b/src/Quasar/Mount/Mimir.purs @@ -18,39 +18,25 @@ module Quasar.Mount.Mimir ( Config , toJSON , fromJSON - , parseDirPath - , module Exports ) where import Prelude -import Data.Path.Pathy as P -import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) -import Data.Either (Either(..)) -import Data.Maybe (Maybe, maybe) -import Data.Path.Pathy (Abs, Dir, Path, Sandboxed, Unsandboxed, ()) -import Quasar.Mount.Common (Host) as Exports - -type Config = Path Abs Dir Sandboxed -sandbox - ∷ forall a - . Path Abs a Unsandboxed - → Maybe (Path Abs a Sandboxed) -sandbox = - map (P.rootDir _) <<< P.sandbox P.rootDir +import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) +import Data.Either (Either, note) +import Pathy (Abs, Dir, Path) +import Quasar.Types (parseQDirPath, printQPath) -parseDirPath ∷ String -> Maybe (Path Abs Dir Sandboxed) -parseDirPath = sandbox <=< P.parseAbsDir +type Config = Path Abs Dir toJSON ∷ Config → Json toJSON config = - let uri = P.printPath config - in "mimir" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject + "mimir" := ("connectionUri" := printQPath config ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON - = maybe (Left "Couldn't sandbox") Right - <<< parseDirPath + = note "Couldn't parse absolute dir path" + <<< parseQDirPath <=< (_ .? "connectionUri") <=< (_ .? "mimir") <=< decodeJson diff --git a/src/Quasar/Mount/MongoDB.purs b/src/Quasar/Mount/MongoDB.purs index 86110ae..3095702 100644 --- a/src/Quasar/Mount/MongoDB.purs +++ b/src/Quasar/Mount/MongoDB.purs @@ -21,29 +21,24 @@ module Quasar.Mount.MongoDB , fromJSON , toURI , fromURI - , module Exports ) where import Prelude import Control.Alt ((<|>)) import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) -import Data.Array as Arr import Data.Bifunctor (lmap) +import Data.Codec (decode, encode) import Data.Either (Either(..)) import Data.Foldable (null) import Data.Maybe (Maybe(..), maybe) import Data.Newtype (class Newtype, unwrap) -import Data.NonEmpty (NonEmpty(..), oneOf) -import Data.Path.Pathy as P import Data.StrMap as SM -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI -import Quasar.Mount.Common (Host, Credentials(..)) as Exports -import Quasar.Mount.Common (Host, Credentials, combineCredentials, extractCredentials) -import Quasar.Types (AnyPath) +import Pathy as P +import Quasar.URI as URI +import URI.Scheme as Scheme -newtype Auth = Auth { path ∷ AnyPath, credentials ∷ Credentials } +newtype Auth = Auth { path ∷ P.AbsPath, credentials ∷ URI.UserPassInfo } derive instance newtypeAuth ∷ Newtype Auth _ derive instance eqAuth ∷ Eq Auth @@ -54,59 +49,52 @@ instance showAuth ∷ Show Auth where "(Auth { path: " <> show path <> ", credentials: " <> show credentials <> " })" type Config = - { hosts ∷ NonEmpty Array Host + { hosts ∷ URI.QURIHosts , auth ∷ Maybe Auth , props ∷ SM.StrMap (Maybe String) } toJSON ∷ Config → Json toJSON config = - let uri = AbsoluteURI.print (toURI config) + let uri = encode URI.mongoURI (toURI config) in "mongodb" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON = fromURI - <=< lmap show <<< AbsoluteURI.parse + <=< lmap show <<< decode URI.mongoURI <=< (_ .? "connectionUri") <=< (_ .? "mongodb") <=< decodeJson -toURI ∷ Config → URI.AbsoluteURI +toURI ∷ Config → URI.MongoURI toURI { hosts, auth, props } = URI.AbsoluteURI - (Just uriScheme) - (URI.HierarchicalPart - (Just - (URI.Authority - (combineCredentials <<< _.credentials <<< unwrap <$> auth) - (oneOf hosts))) + uriScheme + (URI.HierarchicalPartAuth + (URI.Authority + (_.credentials <<< unwrap <$> auth) + hosts) (map (_.path <<< unwrap) auth <|> Just (Left P.rootDir))) (if null props then Nothing - else Just (URI.Query (SM.toUnfoldable props))) + else Just (URI.QueryPairs (SM.toUnfoldable props))) -fromURI ∷ URI.AbsoluteURI → Either String Config -fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth path) query) = do - unless (scheme == Just uriScheme) $ Left "Expected 'mongodb' URL scheme" - hosts ← extractHosts auth - auth' ← case extractCredentials auth, path of - Just credentials, Just p → pure $ Just (Auth { path: p, credentials }) +fromURI ∷ URI.MongoURI → Either String Config +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartNoAuth _) _) = do + Left "Expected 'auth' part in URI" +fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPartAuth (URI.Authority credentials hosts) path) query) = do + unless (scheme == uriScheme) $ Left "Expected 'mongodb' URL scheme" + auth' ← case credentials, path of + Just c, Just p → pure $ Just (Auth { path: p, credentials: c }) Nothing, Nothing → pure $ Nothing Just _, Nothing → Left "User credentials were specified, but no auth database" Nothing, Just p | p /= Left P.rootDir → Left "An auth database was specified, but no user credentials" | otherwise → pure $ Nothing - let props = maybe SM.empty (\(URI.Query qs) → SM.fromFoldable qs) query + let props = maybe SM.empty (\(URI.QueryPairs qs) → SM.fromFoldable qs) query pure { hosts, auth: auth', props } uriScheme ∷ URI.Scheme -uriScheme = URI.Scheme "mongodb" - -extractHosts ∷ Maybe URI.Authority → Either String (NonEmpty Array Host) -extractHosts = maybe err Right <<< (toNonEmpty <=< map getHosts) - where - getHosts (URI.Authority _ hs) = hs - toNonEmpty hs = NonEmpty <$> Arr.head hs <*> Arr.tail hs - err = Left "Host list must not be empty" +uriScheme = Scheme.unsafeFromString "mongodb" diff --git a/src/Quasar/Mount/MongoDB/Gen.purs b/src/Quasar/Mount/MongoDB/Gen.purs index a0543de..0039e0e 100644 --- a/src/Quasar/Mount/MongoDB/Gen.purs +++ b/src/Quasar/Mount/MongoDB/Gen.purs @@ -22,13 +22,14 @@ import Control.Monad.Gen (class MonadGen) import Control.Monad.Gen.Common as GenC import Control.Monad.Rec.Class (class MonadRec) import Data.StrMap.Gen as SMG -import Quasar.Mount.Common.Gen (genAlphaNumericString, genHost, genCredentials, genAnyPath) +import Pathy.Gen (genAbsAnyPath) +import Quasar.Mount.Common.Gen (genAlphaNumericString, genHosts, genCredentials) import Quasar.Mount.MongoDB as MDB genConfig ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MDB.Config genConfig = { hosts: _, auth: _, props: _ } - <$> GenC.genNonEmpty genHost + <$> genHosts <*> GenC.genMaybe genAuth <*> SMG.genStrMap genAlphaNumericString (GenC.genMaybe genAlphaNumericString) @@ -36,5 +37,5 @@ genAuth ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m MDB.Auth genAuth = MDB.Auth <$> ({ path: _, credentials: _ } - <$> genAnyPath + <$> genAbsAnyPath <*> genCredentials) diff --git a/src/Quasar/Mount/SparkHDFS.purs b/src/Quasar/Mount/SparkHDFS.purs index 2be9cfb..cf5c474 100644 --- a/src/Quasar/Mount/SparkHDFS.purs +++ b/src/Quasar/Mount/SparkHDFS.purs @@ -20,7 +20,6 @@ module Quasar.Mount.SparkHDFS , fromJSON , toURI , fromURI - , module Exports ) where import Prelude @@ -28,57 +27,58 @@ import Prelude import Data.Argonaut (Json, (.?), (:=), (~>)) import Data.Argonaut as J import Data.Bifunctor (lmap) -import Data.Either (Either(..)) -import Data.List as L +import Data.Codec (decode, encode) +import Data.Either (Either(..), note) import Data.Maybe (Maybe(..), maybe) import Data.StrMap as SM import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI -import Data.URI.Path (printPath, parseURIPathAbs) import Global (encodeURIComponent, decodeURIComponent) -import Quasar.Mount.Common (Host) as Exports -import Quasar.Mount.Common (Host, extractHost) -import Quasar.Types (DirPath) -import Text.Parsing.StringParser (runParser) +import Pathy (AbsDir) +import Quasar.Types (parseQDirPath, printQPath) +import Quasar.URI as URI +import URI.Scheme as Scheme type Config = - { sparkHost ∷ Host - , hdfsHost ∷ Host - , path ∷ DirPath + { sparkHost ∷ URI.QURIHost' + , hdfsHost ∷ URI.QURIHost' + , path ∷ AbsDir , props ∷ SM.StrMap (Maybe String) } toJSON ∷ Config → Json toJSON config = - let uri = AbsoluteURI.print (toURI config) + let uri = encode URI.qAbsoluteURI (toURI config) in "spark-hdfs" := ("connectionUri" := uri ~> J.jsonEmptyObject) ~> J.jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON = fromURI - <=< lmap show <<< AbsoluteURI.parse + <=< lmap show <<< decode URI.qAbsoluteURI <=< (_ .? "connectionUri") <=< (_ .? "spark-hdfs") <=< J.decodeJson -toURI ∷ Config → URI.AbsoluteURI -toURI cfg = mkURI sparkURIScheme cfg.sparkHost (Just (URI.Query $ requiredProps <> optionalProps)) +toURI ∷ Config → URI.QAbsoluteURI +toURI cfg = + mkURI sparkURIScheme cfg.sparkHost (Just (URI.QueryPairs $ requiredProps <> optionalProps)) where - requiredProps ∷ L.List (Tuple String (Maybe String)) - requiredProps = L.fromFoldable - [ Tuple "hdfsUrl" $ Just $ encodeURIComponent $ AbsoluteURI.print $ mkURI hdfsURIScheme cfg.hdfsHost Nothing - , Tuple "rootPath" $ Just $ printPath (Left cfg.path) + requiredProps ∷ Array (Tuple String (Maybe String)) + requiredProps = + [ Tuple "hdfsUrl" $ Just $ encodeURIComponent $ encode URI.qAbsoluteURI $ mkURI hdfsURIScheme cfg.hdfsHost Nothing + , Tuple "rootPath" $ Just $ printQPath cfg.path ] - optionalProps ∷ L.List (Tuple String (Maybe String)) + optionalProps ∷ Array (Tuple String (Maybe String)) optionalProps = SM.toUnfoldable cfg.props -fromURI ∷ URI.AbsoluteURI → Either String Config -fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth _) query) = do - unless (scheme == Just sparkURIScheme) $ Left "Expected `spark` URL scheme" - sparkHost ← extractHost auth - let props = maybe SM.empty (\(URI.Query qs) → SM.fromFoldable qs) query +fromURI ∷ URI.QAbsoluteURI → Either String Config +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartNoAuth _) _) = do + Left "Expected 'auth' part in URI" +fromURI (URI.AbsoluteURI _ (URI.HierarchicalPartAuth (URI.Authority _ Nothing) _) _) = do + Left "Expected 'host' part to be present in URL" +fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPartAuth (URI.Authority _ (Just sparkHost)) _) query) = do + unless (scheme == sparkURIScheme) $ Left "Expected `spark` URL scheme" + let props = maybe SM.empty (\(URI.QueryPairs qs) → SM.fromFoldable qs) query Tuple hdfsHost props' ← case SM.pop "hdfsUrl" props of Just (Tuple (Just value) rest) → do @@ -88,31 +88,30 @@ fromURI (URI.AbsoluteURI scheme (URI.HierarchicalPart auth _) query) = do Tuple path props'' ← case SM.pop "rootPath" props' of Just (Tuple (Just value) rest) → do - value' ← lmap show $ runParser parseURIPathAbs value - dirPath ← case value' of - Left dp → pure dp - Right _ → Left "Expected `rootPath` to be a directory path" + dirPath ← note "Expected `rootPath` to be a directory path" $ parseQDirPath value pure (Tuple dirPath rest) _ → Left "Expected `rootPath` query parameter" pure { sparkHost, hdfsHost, path, props: props'' } -mkURI ∷ URI.Scheme → Host → Maybe URI.Query → URI.AbsoluteURI +mkURI ∷ URI.Scheme → URI.QURIHost' → Maybe URI.QQuery → URI.QAbsoluteURI mkURI scheme host params = URI.AbsoluteURI - (Just scheme) - (URI.HierarchicalPart (Just (URI.Authority Nothing (pure host))) Nothing) + (scheme) + (URI.HierarchicalPartAuth (URI.Authority Nothing (Just host)) Nothing) params -extractHost' ∷ URI.Scheme → String → Either String Host -extractHost' scheme@(URI.Scheme name) uri = do - URI.AbsoluteURI scheme' (URI.HierarchicalPart auth _) _ ← - lmap show $ AbsoluteURI.parse uri - unless (scheme' == Just scheme) $ Left $ "Expected '" <> name <> "' URL scheme" - extractHost auth +extractHost' ∷ URI.Scheme → String → Either String URI.QURIHost' +extractHost' scheme uri = do + URI.AbsoluteURI scheme' hierPart _ ← lmap show $ decode URI.qAbsoluteURI uri + unless (scheme' == scheme) $ Left $ "Expected '" <> Scheme.print scheme <> "' URL scheme" + case hierPart of + URI.HierarchicalPartNoAuth _ → Left $ "Expected auth part to be present in URL" + URI.HierarchicalPartAuth (URI.Authority _ Nothing) _ → Left "Expected 'host' part to be present in URL" + URI.HierarchicalPartAuth (URI.Authority _ (Just host)) _ → pure host sparkURIScheme ∷ URI.Scheme -sparkURIScheme = URI.Scheme "spark" +sparkURIScheme = Scheme.unsafeFromString "spark" hdfsURIScheme ∷ URI.Scheme -hdfsURIScheme = URI.Scheme "hdfs" +hdfsURIScheme = Scheme.unsafeFromString "hdfs" diff --git a/src/Quasar/Mount/SparkHDFS/Gen.purs b/src/Quasar/Mount/SparkHDFS/Gen.purs index df59d06..7d6271a 100644 --- a/src/Quasar/Mount/SparkHDFS/Gen.purs +++ b/src/Quasar/Mount/SparkHDFS/Gen.purs @@ -22,13 +22,13 @@ import Control.Monad.Gen (class MonadGen) import Control.Monad.Gen.Common as GenC import Control.Monad.Rec.Class (class MonadRec) import Data.StrMap.Gen as SMG -import Quasar.Mount.Common.Gen (genAlphaNumericString, genHost, genAbsDirPath) +import Quasar.Mount.Common.Gen (genAlphaNumericString, genHost', genAbsDirPath) import Quasar.Mount.SparkHDFS as SHDFS genConfig ∷ ∀ m. MonadGen m ⇒ MonadRec m ⇒ m SHDFS.Config genConfig = { sparkHost: _, hdfsHost: _, path: _, props: _ } - <$> genHost - <*> genHost + <$> genHost' + <*> genHost' <*> genAbsDirPath <*> SMG.genStrMap genAlphaNumericString (GenC.genMaybe genAlphaNumericString) diff --git a/src/Quasar/Mount/SparkLocal.purs b/src/Quasar/Mount/SparkLocal.purs index 9d97e38..dddba18 100644 --- a/src/Quasar/Mount/SparkLocal.purs +++ b/src/Quasar/Mount/SparkLocal.purs @@ -18,39 +18,25 @@ module Quasar.Mount.SparkLocal ( Config , toJSON , fromJSON - , parseDirPath - , module Exports ) where import Prelude -import Data.Path.Pathy as P -import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) -import Data.Either (Either(..)) -import Data.Maybe (Maybe, maybe) -import Data.Path.Pathy (Abs, Dir, Path, Sandboxed, Unsandboxed, ()) -import Quasar.Mount.Common (Host) as Exports - -type Config = Path Abs Dir Sandboxed -sandbox - ∷ forall a - . Path Abs a Unsandboxed - → Maybe (Path Abs a Sandboxed) -sandbox = - map (P.rootDir _) <<< P.sandbox P.rootDir +import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (:=), (~>)) +import Data.Either (Either, note) +import Pathy (AbsDir) +import Quasar.Types (parseQDirPath, printQPath) -parseDirPath ∷ String -> Maybe (Path Abs Dir Sandboxed) -parseDirPath = sandbox <=< P.parseAbsDir +type Config = AbsDir toJSON ∷ Config → Json toJSON config = - let uri = P.printPath config - in "spark-local" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject + "spark-local" := ("connectionUri" := printQPath config ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON - = maybe (Left "Couldn't sandbox") Right - <<< parseDirPath + = note "Couldn't parse absolute dir path" + <<< parseQDirPath <=< (_ .? "connectionUri") <=< (_ .? "spark-local") <=< decodeJson diff --git a/src/Quasar/Mount/View.purs b/src/Quasar/Mount/View.purs index 76bbd59..73c287b 100644 --- a/src/Quasar/Mount/View.purs +++ b/src/Quasar/Mount/View.purs @@ -20,21 +20,20 @@ import Prelude import Data.Argonaut (Json, decodeJson, jsonEmptyObject, (.?), (~>), (:=)) import Data.Bifunctor (bimap, lmap) -import Data.Either (Either(..)) +import Data.Codec (decode, encode) +import Data.Either (Either(..), note) import Data.Foldable (foldMap) -import Data.List ((:), List(..)) -import Data.List as List import Data.Maybe (Maybe(..), maybe) -import Data.StrMap as SM import Data.String as Str +import Data.StrMap as SM import Data.Tuple (Tuple(..), lookup) -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI import Quasar.Types (Vars) +import Quasar.URI as URI import SqlSquared (SqlQuery) import SqlSquared as Sql import Text.Parsing.Parser (ParseError(..)) import Text.Parsing.Parser.Pos (Position(..)) +import URI.Scheme as Scheme type Config = { query ∷ SqlQuery @@ -43,34 +42,34 @@ type Config = toJSON ∷ Config → Json toJSON config = - let uri = AbsoluteURI.print (toURI config) + let uri = encode URI.qAbsoluteURI (toURI config) in "view" := ("connectionUri" := uri ~> jsonEmptyObject) ~> jsonEmptyObject fromJSON ∷ Json → Either String Config fromJSON = fromURI - <=< lmap show <<< AbsoluteURI.parse + <=< lmap show <<< decode URI.qAbsoluteURI <=< (_ .? "connectionUri") <=< (_ .? "view") <=< decodeJson -toURI ∷ Config → URI.AbsoluteURI +toURI ∷ Config → URI.QAbsoluteURI toURI { query, vars } = URI.AbsoluteURI - (Just uriScheme) - (URI.HierarchicalPart Nothing Nothing) - (Just (URI.Query props)) + (uriScheme) + (URI.HierarchicalPartNoAuth Nothing) + (Just (URI.QueryPairs props)) where - props ∷ List (Tuple String (Maybe String)) + props ∷ Array (Tuple String (Maybe String)) props - = Tuple "q" (Just $ Sql.printQuery query) - : (bimap ("var." <> _) Just <$> SM.toUnfoldable vars) + = [ Tuple "q" (Just $ Sql.printQuery query) ] + <> (bimap ("var." <> _) Just <$> SM.toUnfoldable vars) -fromURI ∷ URI.AbsoluteURI → Either String Config +fromURI ∷ URI.QAbsoluteURI → Either String Config fromURI (URI.AbsoluteURI scheme _ query) = do - unless (scheme == Just uriScheme) $ Left "Expected 'sql2' URL scheme" - let queryMap = maybe List.Nil (\(URI.Query q) → q) query - query' ← maybe (Left "Expected 'q' query variable") pure (extractQuery queryMap) + unless (scheme == uriScheme) $ Left "Expected 'sql2' URL scheme" + let queryMap = maybe [] (\(URI.QueryPairs q) → q) query + query' ← note "Expected 'q' query variable" $ extractQuery queryMap q ← Sql.parseQuery query' # lmap \(ParseError err (Position { line , column })) → "Expected 'q' query variable to contain valid query, " <> "but at line " <> show line <> "and column " <> show column <> " got parse error: \n" <> err @@ -78,11 +77,11 @@ fromURI (URI.AbsoluteURI scheme _ query) = do pure { query: q, vars } uriScheme ∷ URI.Scheme -uriScheme = URI.Scheme "sql2" +uriScheme = Scheme.unsafeFromString "sql2" -extractQuery ∷ List (Tuple String (Maybe String)) → Maybe String +extractQuery ∷ Array (Tuple String (Maybe String)) → Maybe String extractQuery= join <<< lookup "q" -extractVar ∷ Tuple String (Maybe String) → List (Tuple String String) -extractVar (Tuple key val) = maybe Nil List.singleton $ +extractVar ∷ Tuple String (Maybe String) → Array (Tuple String String) +extractVar (Tuple key val) = maybe [] pure $ Tuple <$> Str.stripPrefix (Str.Pattern "var.") key <*> val diff --git a/src/Quasar/Paths.purs b/src/Quasar/Paths.purs index 42ffe09..6ca55ae 100644 --- a/src/Quasar/Paths.purs +++ b/src/Quasar/Paths.purs @@ -16,31 +16,32 @@ limitations under the License. module Quasar.Paths where -import Data.Path.Pathy (RelDir, RelFile, Sandboxed, file, dir, ()) +import Pathy (RelDir, RelFile, file, dir, ()) +import Data.Symbol (SProxy(..)) -upload ∷ RelFile Sandboxed -upload = file "upload" +upload ∷ RelFile +upload = file (SProxy ∷ SProxy "upload") -metadata ∷ RelDir Sandboxed -metadata = dir "metadata" dir "fs" +metadata ∷ RelDir +metadata = dir (SProxy ∷ SProxy "metadata") dir (SProxy ∷ SProxy "fs") -metastore ∷ RelFile Sandboxed -metastore = file "metastore" +metastore ∷ RelFile +metastore = file (SProxy ∷ SProxy "metastore") -mount ∷ RelDir Sandboxed -mount = dir "mount" dir "fs" +mount ∷ RelDir +mount = dir (SProxy ∷ SProxy "mount") dir (SProxy ∷ SProxy "fs") -data_ ∷ RelDir Sandboxed -data_ = dir "data" dir "fs" +data_ ∷ RelDir +data_ = dir (SProxy ∷ SProxy "data") dir (SProxy ∷ SProxy "fs") -query ∷ RelDir Sandboxed -query = dir "query" dir "fs" +query ∷ RelDir +query = dir (SProxy ∷ SProxy "query") dir (SProxy ∷ SProxy "fs") -compile ∷ RelDir Sandboxed -compile = dir "compile" dir "fs" +compile ∷ RelDir +compile = dir (SProxy ∷ SProxy "compile") dir (SProxy ∷ SProxy "fs") -serverInfo ∷ RelFile Sandboxed -serverInfo = dir "server" file "info" +serverInfo ∷ RelFile +serverInfo = dir (SProxy ∷ SProxy "server") file (SProxy ∷ SProxy "info") -invoke ∷ RelDir Sandboxed -invoke = dir "invoke" dir "fs" +invoke ∷ RelDir +invoke = dir (SProxy ∷ SProxy "invoke") dir (SProxy ∷ SProxy "fs") diff --git a/src/Quasar/QuasarF.purs b/src/Quasar/QuasarF.purs index 803186e..53c9639 100644 --- a/src/Quasar/QuasarF.purs +++ b/src/Quasar/QuasarF.purs @@ -26,6 +26,7 @@ import DOM.File.Types (Blob) import Data.Argonaut (JArray) import Data.Maybe (Maybe(..)) import Data.Time.Duration (Seconds) +import Pathy (AbsDir, AbsFile, AbsPath) import Quasar.Data (QData) import Quasar.Data.Json (PrecisionMode(..)) import Quasar.Data.Json.Extended (EJson, resultsAsEJson) @@ -36,28 +37,28 @@ import Quasar.Mount (MountConfig(..)) import Quasar.Mount.View as View import Quasar.Query.OutputMeta (OutputMeta) import Quasar.ServerInfo (ServerInfo) -import Quasar.Types (AnyPath, FilePath, DirPath, Pagination, Vars, CompileResultR) +import Quasar.Types (Pagination, Vars, CompileResultR) import SqlSquared (SqlQuery) data QuasarF a = ServerInfo (ServerInfo :~> a) - | ReadQuery PrecisionMode DirPath SqlQuery Vars (Maybe Pagination) (JArray :~> a) - | WriteQuery DirPath FilePath SqlQuery Vars (OutputMeta :~> a) - | CompileQuery DirPath SqlQuery Vars (CompileResultR :~> a) - | FileMetadata FilePath (Unit :~> a) - | DirMetadata DirPath (Maybe Pagination) ((Array QResource) :~> a) - | ReadFile PrecisionMode FilePath (Maybe Pagination) (JArray :~> a) - | WriteFile FilePath QData (Unit :~> a) - | WriteDir DirPath Blob (Unit :~> a) - | AppendFile FilePath QData (Unit :~> a) - | InvokeFile PrecisionMode FilePath Vars (Maybe Pagination) (JArray :~> a) - | DeleteData AnyPath (Unit :~> a) - | MoveData AnyPath AnyPath (Unit :~> a) - | GetMount AnyPath (MountConfig :~> a) - | CreateMount AnyPath MountConfig (Maybe Seconds) (Unit :~> a) - | UpdateMount AnyPath MountConfig (Maybe Seconds) (Unit :~> a) - | MoveMount AnyPath AnyPath (Unit :~> a) - | DeleteMount AnyPath (Unit :~> a) + | ReadQuery PrecisionMode AbsDir SqlQuery Vars (Maybe Pagination) (JArray :~> a) + | WriteQuery AbsDir AbsFile SqlQuery Vars (OutputMeta :~> a) + | CompileQuery AbsDir SqlQuery Vars (CompileResultR :~> a) + | FileMetadata AbsFile (Unit :~> a) + | DirMetadata AbsDir (Maybe Pagination) ((Array QResource) :~> a) + | ReadFile PrecisionMode AbsFile (Maybe Pagination) (JArray :~> a) + | WriteFile AbsFile QData (Unit :~> a) + | WriteDir AbsDir Blob (Unit :~> a) + | AppendFile AbsFile QData (Unit :~> a) + | InvokeFile PrecisionMode AbsFile Vars (Maybe Pagination) (JArray :~> a) + | DeleteData AbsPath (Unit :~> a) + | MoveData AbsPath AbsPath (Unit :~> a) + | GetMount AbsPath (MountConfig :~> a) + | MoveMount AbsPath AbsPath (Unit :~> a) + | DeleteMount AbsPath (Unit :~> a) + | CreateMount AbsPath MountConfig (Maybe Seconds) (Unit :~> a) + | UpdateMount AbsPath MountConfig (Maybe Seconds) (Unit :~> a) | GetMetastore (Metastore () :~> a) | PutMetastore { initialize ∷ Boolean, metastore ∷ Metastore (password ∷ String) } (Unit :~> a) @@ -73,7 +74,7 @@ serverInfo = readQuery ∷ PrecisionMode - → DirPath + → AbsDir → SqlQuery → Vars → Maybe Pagination @@ -82,7 +83,7 @@ readQuery mode path sql vars pagination = ReadQuery mode path sql vars pagination id readQueryEJson - ∷ DirPath + ∷ AbsDir → SqlQuery → Vars → Maybe Pagination @@ -91,8 +92,8 @@ readQueryEJson path sql vars pagination = readQuery Precise path sql vars pagination <#> resultsAsEJson writeQuery - ∷ DirPath - → FilePath + ∷ AbsDir + → AbsFile → SqlQuery → Vars → QuasarFE OutputMeta @@ -100,7 +101,7 @@ writeQuery path file sql vars = WriteQuery path file sql vars id compileQuery - ∷ DirPath + ∷ AbsDir → SqlQuery → Vars → QuasarFE CompileResultR @@ -108,13 +109,13 @@ compileQuery path sql vars = CompileQuery path sql vars id fileMetadata - ∷ FilePath + ∷ AbsFile → QuasarFE Unit fileMetadata path = FileMetadata path id dirMetadata - ∷ DirPath + ∷ AbsDir → Maybe Pagination → QuasarFE (Array QResource) dirMetadata path pagination = @@ -122,35 +123,35 @@ dirMetadata path pagination = readFile ∷ PrecisionMode - → FilePath + → AbsFile → Maybe Pagination → QuasarFE JArray readFile mode path pagination = ReadFile mode path pagination id readFileEJson - ∷ FilePath + ∷ AbsFile → Maybe Pagination → QuasarFE (Array EJson) readFileEJson path pagination = readFile Precise path pagination <#> resultsAsEJson writeFile - ∷ FilePath + ∷ AbsFile → QData → QuasarFE Unit writeFile path content = WriteFile path content id writeDir - ∷ DirPath + ∷ AbsDir → Blob → QuasarFE Unit writeDir path content = WriteDir path content id appendFile - ∷ FilePath + ∷ AbsFile → QData → QuasarFE Unit appendFile path content = @@ -158,7 +159,7 @@ appendFile path content = invokeFile ∷ PrecisionMode - → FilePath + → AbsFile → Vars → Maybe Pagination → QuasarFE JArray @@ -166,7 +167,7 @@ invokeFile mode path vars pagination = InvokeFile mode path vars pagination id invokeFileEJson - ∷ FilePath + ∷ AbsFile → Vars → Maybe Pagination → QuasarFE (Array EJson) @@ -174,40 +175,40 @@ invokeFileEJson path vars pagination = invokeFile Precise path vars pagination <#> resultsAsEJson deleteData - ∷ AnyPath + ∷ AbsPath → QuasarFE Unit deleteData path = DeleteData path id moveData - ∷ AnyPath - → AnyPath + ∷ AbsPath + → AbsPath → QuasarFE Unit moveData from to = MoveData from to id getMount - ∷ AnyPath + ∷ AbsPath → QuasarFE MountConfig getMount path = GetMount path id createMount - ∷ AnyPath + ∷ AbsPath → MountConfig → QuasarFE Unit createMount path config = CreateMount path config Nothing id updateMount - ∷ AnyPath + ∷ AbsPath → MountConfig → QuasarFE Unit updateMount path config = UpdateMount path config Nothing id createCachedView - ∷ AnyPath + ∷ AbsPath → View.Config → Seconds → QuasarFE Unit @@ -215,7 +216,7 @@ createCachedView path config maxAge = CreateMount path (ViewConfig config) (Just maxAge) id updateCachedView - ∷ AnyPath + ∷ AbsPath → View.Config → Seconds → QuasarFE Unit @@ -223,14 +224,14 @@ updateCachedView path config maxAge = UpdateMount path (ViewConfig config) (Just maxAge) id moveMount - ∷ AnyPath - → AnyPath + ∷ AbsPath + → AbsPath → QuasarFE Unit moveMount from to = MoveMount from to id deleteMount - ∷ AnyPath + ∷ AbsPath → QuasarFE Unit deleteMount path = DeleteMount path id @@ -242,4 +243,3 @@ putMetastore ∷ { initialize ∷ Boolean, metastore ∷ Metastore (password ∷ String) } → QuasarFE Unit putMetastore ms = PutMetastore ms id - diff --git a/src/Quasar/QuasarF/Interpreter/Affjax.purs b/src/Quasar/QuasarF/Interpreter/Affjax.purs index b19cdfc..65cdcbd 100644 --- a/src/Quasar/QuasarF/Interpreter/Affjax.purs +++ b/src/Quasar/QuasarF/Interpreter/Affjax.purs @@ -27,24 +27,24 @@ import Control.Monad.Eff.Exception (Error, error) import Control.Monad.Free (Free) import Data.Argonaut (Json, JObject, jsonEmptyObject, (:=), (~>)) import Data.Array (catMaybes) -import Data.Bifunctor (lmap) +import Data.Bifunctor (bimap, lmap) import Data.Either (Either(..), either) import Data.Foldable (class Foldable, foldl, foldMap) import Data.Functor.Coproduct (Coproduct) import Data.HTTP.Method (Method(..)) import Data.Int as Int -import Data.List (singleton) -import Data.Maybe (Maybe(..), maybe) +import Data.Maybe (Maybe(..)) import Data.MediaType.Common (applicationJSON) import Data.Monoid (mempty) -import Data.Path.Pathy (peel, printPath, rootDir, runDirName, runFileName) +import Data.Newtype (un) +import Data.String.NonEmpty (toString) import Data.StrMap as SM import Data.Time.Duration (Seconds(..)) import Data.Tuple (Tuple(..), fst, snd) -import Data.URI as URI import Network.HTTP.Affjax.Request (RequestContent, toRequest) import Network.HTTP.AffjaxF as AXF import Network.HTTP.RequestHeader as Req +import Pathy (Name(..), AbsDir, peel, peelFile, rootDir) import Quasar.ConfigF as CF import Quasar.Data.Json as Json import Quasar.Data.MediaTypes (applicationZip) @@ -52,12 +52,14 @@ import Quasar.FS.DirMetadata as DirMetadata import Quasar.Metastore as Metastore import Quasar.Mount as Mount import Quasar.Paths as Paths -import Quasar.QuasarF (QuasarF(..), DirPath) +import Quasar.QuasarF (QuasarF(..)) import Quasar.QuasarF.Interpreter.Config (Config) import Quasar.QuasarF.Interpreter.Internal (defaultRequest, delete, get, jsonResult, mkFSUrl, mkRequest, mkUrl, put, strResult, toPageParams, toVarParams, unitResult) import Quasar.Query.OutputMeta as QueryOutputMeta import Quasar.ServerInfo as ServerInfo +import Quasar.Types (printQPath) import Quasar.Types as QT +import Quasar.URI as URI import SqlSquared as Sql type M r = Free (Coproduct (CF.ConfigF (Config r)) (AXF.AffjaxFP RequestContent String)) @@ -86,7 +88,7 @@ eval = case _ of }) WriteQuery path file sql vars k → do - let destHeader = Tuple "Destination" (printPath file) + let destHeader = Tuple "Destination" (printQPath file) url ← mkFSUrl Paths.query (Left path) (headerParams [destHeader] <> toVarParams vars) k <$> mkRequest writeQueryResult (AXF.affjax $ defaultRequest @@ -142,7 +144,7 @@ eval = case _ of InvokeFile mode path vars pagination k → do -- We can't use toVarParams here, as the format is different for invokeFile, -- instead of var.x=3 it's just x=3 - url ← mkFSUrl Paths.invoke (Right path) (URI.Query (map Just <$> SM.toUnfoldable vars) <> toPageParams pagination) + url ← mkFSUrl Paths.invoke (Right path) (URI.QueryPairs (map Just <$> SM.toUnfoldable vars) <> toPageParams pagination) k <$> mkRequest jsonResult (AXF.affjax defaultRequest { url = url @@ -153,7 +155,7 @@ eval = case _ of k <$> (mkRequest unitResult <<< delete =<< mkFSUrl Paths.data_ path mempty) MoveData fromPath toPath k → do - let destHeader = Tuple "Destination" (either printPath printPath toPath) + let destHeader = Tuple "Destination" (either printQPath printQPath toPath) url ← mkFSUrl Paths.data_ fromPath (headerParams [destHeader]) k <$> mkRequest unitResult (AXF.affjax defaultRequest @@ -162,11 +164,12 @@ eval = case _ of }) CreateMount path config mbMaxAge k → do - let pathParts = either peel peel path - parentDir = maybe rootDir fst pathParts - name = maybe "" (either runDirName runFileName <<< snd) pathParts - filenameHeader = Tuple "X-File-Name" name - url ← mkFSUrl Paths.mount (Left parentDir) (headerParams [filenameHeader]) + let + Tuple parentDir name = case bimap peel peelFile path of + Left Nothing → Tuple rootDir "" + Left (Just dp) → dp <#> un Name >>> toString >>> (_ <> "/") + Right fp → fp <#> un Name >>> toString + url ← mkFSUrl Paths.mount (Left parentDir) (headerParams [Tuple "X-File-Name" name]) k <$> mkRequest unitResult (AXF.affjax defaultRequest { url = url @@ -189,7 +192,7 @@ eval = case _ of k <$> (mkRequest mountConfigResult <<< get =<< mkFSUrl Paths.mount path mempty) MoveMount fromPath toPath k → do - let destHeader = Tuple "Destination" (either printPath printPath toPath) + let destHeader = Tuple "Destination" (either printQPath printQPath toPath) url ← mkFSUrl Paths.mount fromPath (headerParams [destHeader]) k <$> mkRequest unitResult (AXF.affjax defaultRequest @@ -205,18 +208,18 @@ eval = case _ of k <$> mkRequest metastoreResult (get url) PutMetastore { initialize, metastore } k → do - let query = if initialize then URI.Query (singleton (Tuple "initialize" Nothing)) else mempty - url ← mkUrl (Right Paths.metastore) query + let query = if initialize then [Tuple "initialize" Nothing] else [] + url ← mkUrl (Right Paths.metastore) (URI.QueryPairs query) k <$> (mkRequest unitResult $ put url $ snd (toRequest (Metastore.toJSON metastore))) -serverInfoResult ∷ String -> Either Error ServerInfo.ServerInfo +serverInfoResult ∷ String → Either Error ServerInfo.ServerInfo serverInfoResult = lmap error <$> ServerInfo.fromJSON <=< jsonResult writeQueryResult ∷ String → Either Error QueryOutputMeta.OutputMeta writeQueryResult = lmap error <$> QueryOutputMeta.fromJSON <=< jsonResult -resourcesResult ∷ DirPath → String → Either Error DirMetadata.DirMetadata +resourcesResult ∷ AbsDir → String → Either Error DirMetadata.DirMetadata resourcesResult path = lmap error <$> DirMetadata.fromJSON path <=< jsonResult mountConfigResult ∷ String → Either Error Mount.MountConfig @@ -228,10 +231,10 @@ fileMetaResult = map (\(_ ∷ JObject) → unit) <<< jsonResult metastoreResult ∷ String → Either Error (Metastore.Metastore ()) metastoreResult = lmap error <$> Metastore.fromJSON <=< jsonResult -querySingleton ∷ String → String → URI.Query -querySingleton k v = URI.Query $ singleton $ Tuple k (Just v) +querySingleton ∷ String → String → URI.QQuery +querySingleton k v = URI.QueryPairs [ Tuple k (Just v) ] -headerParams ∷ ∀ f. Foldable f ⇒ f (Tuple String String) → URI.Query +headerParams ∷ ∀ f. Foldable f ⇒ f (Tuple String String) → URI.QQuery headerParams ps = querySingleton "request-headers" (show (foldl go jsonEmptyObject ps)) where go ∷ Json → Tuple String String → Json diff --git a/src/Quasar/QuasarF/Interpreter/Config.purs b/src/Quasar/QuasarF/Interpreter/Config.purs index ed6f477..4765942 100644 --- a/src/Quasar/QuasarF/Interpreter/Config.purs +++ b/src/Quasar/QuasarF/Interpreter/Config.purs @@ -18,10 +18,10 @@ module Quasar.QuasarF.Interpreter.Config where import Data.Either (Either) import Data.Maybe (Maybe) -import Data.Path.Pathy (AbsDir, RelDir, Sandboxed, Unsandboxed) -import Data.URI as URI +import Pathy (AbsDir, RelDir) +import Quasar.URI as URI -type AbsBasePath = { scheme ∷ URI.Scheme, authority ∷ Maybe URI.Authority, path ∷ AbsDir Sandboxed } -type BasePath = Either AbsBasePath (RelDir Unsandboxed) +type AbsBasePath = { scheme ∷ URI.Scheme, authority ∷ Maybe URI.QAuthority, path ∷ AbsDir } +type BasePath = Either AbsBasePath RelDir type Config r = { basePath ∷ BasePath | r } diff --git a/src/Quasar/QuasarF/Interpreter/Internal.purs b/src/Quasar/QuasarF/Interpreter/Internal.purs index 9245be6..281eb14 100644 --- a/src/Quasar/QuasarF/Interpreter/Internal.purs +++ b/src/Quasar/QuasarF/Interpreter/Internal.purs @@ -38,32 +38,31 @@ import Data.Argonaut as Json import Data.Argonaut.Decode.Combinators ((.?), (.??)) import Data.Array as Array import Data.Bifunctor (bimap, lmap) +import Data.Codec (encode) import Data.Either (Either(..), either) import Data.Foldable (oneOf) import Data.Functor.Coproduct (Coproduct, left, right) import Data.HTTP.Method (Method(..)) -import Data.List (List(..), (:)) import Data.Maybe (Maybe(..), fromMaybe) import Data.Monoid (mempty) -import Data.Path.Pathy (Abs, AnyPath, Path, Rel, RelDir, RelPath, Sandboxed, dir, file, relativeTo, rootDir, unsandbox, ()) -import Data.StrMap as SM -import Data.String as Str import Data.String as String +import Data.StrMap (StrMap) +import Data.StrMap as StrMap import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Data.URI.URIRef as URIRef import Network.HTTP.Affjax as AX import Network.HTTP.Affjax.Request (RequestContent) import Network.HTTP.AffjaxF as AXF import Network.HTTP.ResponseHeader as RH import Network.HTTP.StatusCode (StatusCode(..)) +import Pathy (class IsDirOrFile, Abs, AbsPath, Path, Rel, RelDir, RelPath, relativeTo, rootDir, ()) import Quasar.ConfigF as CF import Quasar.Error.Compilation (CompilationError(..)) import Quasar.QuasarF (Pagination, QError(..), PDFError(..), UnauthorizedDetails(..)) import Quasar.QuasarF.Interpreter.Config (Config) -import Unsafe.Coerce (unsafeCoerce) +import Quasar.URI as URI type AXFP = AXF.AffjaxFP RequestContent String +type AjaxM r a = Free (Coproduct (CF.ConfigF (Config r)) AXFP) a ask ∷ ∀ c r. Free (Coproduct (CF.ConfigF c) r) c ask = liftF $ left $ CF.GetConfig id @@ -77,13 +76,15 @@ strResult = Right unitResult ∷ String → Either Error Unit unitResult = const (Right unit) -toVarParams ∷ SM.StrMap String → URI.Query -toVarParams = URI.Query <<< map (bimap ("var." <> _) Just) <<< SM.toUnfoldable +toVarParams ∷ StrMap String → URI.QQuery +toVarParams = URI.QueryPairs <<< map (bimap ("var." <> _) Just) <<< StrMap.toUnfoldable -toPageParams ∷ Maybe Pagination → URI.Query +toPageParams ∷ Maybe Pagination → URI.QQuery toPageParams Nothing = mempty -toPageParams (Just { offset, limit }) - = URI.Query $ Tuple "offset" (Just (show offset)) : Tuple "limit" (Just (show limit)) : Nil +toPageParams (Just { offset, limit }) = URI.QueryPairs + [ Tuple "offset" (Just (show offset)) + , Tuple "limit" (Just (show limit)) + ] defaultRequest ∷ AX.AffjaxRequest RequestContent defaultRequest = AX.defaultRequest { content = Nothing } @@ -98,54 +99,42 @@ delete ∷ AX.URL → AXF.AffjaxF RequestContent String delete u = AXF.affjax (defaultRequest { method = Left DELETE, url = u }) mkFSUrl - ∷ ∀ s s' r - . RelDir s - → AnyPath Abs s' - → URI.Query - → Free (Coproduct (CF.ConfigF (Config r)) AXFP) String -mkFSUrl relDir fsPath q = do - uri ← URIRef.print <$> mkFSUrl' relDir fsPath q - pure uri - -mkFSUrl' - ∷ ∀ s s' r - . RelDir s - → AnyPath Abs s' - → URI.Query - → Free (Coproduct (CF.ConfigF (Config r)) AXFP) URI.URIRef -mkFSUrl' relDir fsPath = mkUrl' (bimap (baseify (dir "/")) (baseify (file "")) fsPath) + ∷ ∀ r + . RelDir + → AbsPath + → URI.QQuery + → AjaxM r String +mkFSUrl relDir fsPath q = mkUrl (bimap baseify baseify fsPath) q where - baseify ∷ ∀ b. Path Rel b s → Path Abs b s' → Path Rel b s - baseify x p = relDir fromMaybe x (p `relativeTo` rootDir) + baseify ∷ ∀ b. IsDirOrFile b ⇒ Path Abs b → Path Rel b + baseify p = relDir p `relativeTo` rootDir -mkUrl ∷ ∀ s r. RelPath s → URI.Query → Free (Coproduct (CF.ConfigF (Config r)) AXFP) String -mkUrl relPath q = URIRef.print <$> mkUrl' relPath q +mkUrl ∷ ∀ r. RelPath → URI.QQuery → AjaxM r String +mkUrl relPath q = encode URI.qURIRef <$> mkUrl' relPath q -mkUrl' ∷ ∀ s r. RelPath s → URI.Query → Free (Coproduct (CF.ConfigF (Config r)) AXFP) URI.URIRef +mkUrl' ∷ ∀ r. RelPath → URI.QQuery → AjaxM r URI.QURIRef mkUrl' relPath q = do { basePath } ← ask pure (bimap toURI toRelativeRef basePath) where toURI { scheme, authority, path } = - URI.URI - (Just scheme) - (URI.HierarchicalPart - authority - (Just (bimap ((path _) <<< sandbox) ((path _) <<< sandbox) relPath))) - (if q == mempty then Nothing else Just q) - Nothing - - sandbox ∷ ∀ a b. Path a b s → Path a b Sandboxed - sandbox = unsafeCoerce - + let + hierPath = (Just (bimap (path _) (path _) relPath)) + hierPart = case authority of + Nothing → URI.HierarchicalPartNoAuth hierPath + Just authority' → URI.HierarchicalPartAuth authority' hierPath + query = if q == mempty then Nothing else Just q + in URI.URI scheme hierPart query Nothing + + toRelativeRef ∷ RelDir → URI.QRelativeRef toRelativeRef relDir = URI.RelativeRef - (URI.RelativePart - Nothing - (Just (bimap ((relDir _) <<< unsandbox) ((relDir _) <<< unsandbox) relPath))) + (URI.RelativePartNoAuth + (Just $ Right (bimap (relDir _) (relDir _) relPath))) (if q == mempty then Nothing else Just q) Nothing + mkRequest ∷ ∀ a l . (String → Either Error a) @@ -179,7 +168,7 @@ handleResult f = Left err → Left (Error err) where isWWWAuthenticate ∷ RH.ResponseHeader → Boolean - isWWWAuthenticate = eq "www-authenticate" <<< Str.toLower <<< RH.responseHeaderName + isWWWAuthenticate = eq "www-authenticate" <<< String.toLower <<< RH.responseHeaderName hush ∷ ∀ a b. Either a b → Maybe b hush = either (const Nothing) Just @@ -230,4 +219,4 @@ parseHumanReadableError json = ]) where wrapError ∷ Json.Json → Json.JObject - wrapError = SM.singleton "error" + wrapError = StrMap.singleton "error" diff --git a/src/Quasar/Query/OutputMeta.purs b/src/Quasar/Query/OutputMeta.purs index 4b9f048..fcf344b 100644 --- a/src/Quasar/Query/OutputMeta.purs +++ b/src/Quasar/Query/OutputMeta.purs @@ -19,14 +19,12 @@ module Quasar.Query.OutputMeta where import Prelude import Data.Argonaut (Json, JArray, decodeJson, (.?)) -import Data.Either (Either(..)) -import Data.Maybe (Maybe, maybe) -import Data.Path.Pathy (parseAbsFile, rootDir, sandbox, ()) - -import Quasar.Types (FilePath) +import Data.Either (Either, note) +import Pathy (AbsFile) +import Quasar.Types (parseQFilePath) type OutputMeta = - { out ∷ FilePath + { out ∷ AbsFile , phases ∷ JArray } @@ -34,9 +32,6 @@ fromJSON ∷ Json → Either String OutputMeta fromJSON json = do obj ← decodeJson json path ← obj .? "out" - out ← maybe (Left "Could not parse 'out' path") Right $ parsePath path + out ← note "Could not parse 'out' path" $ parseQFilePath path phases ← obj .? "phases" pure { out, phases } - where - parsePath ∷ String -> Maybe FilePath - parsePath = map (rootDir _) <<< sandbox rootDir <=< parseAbsFile diff --git a/src/Quasar/Types.purs b/src/Quasar/Types.purs index a72eaef..5e844af 100644 --- a/src/Quasar/Types.purs +++ b/src/Quasar/Types.purs @@ -19,25 +19,28 @@ module Quasar.Types where import Prelude import Control.Alt ((<|>)) - import Data.Argonaut (class DecodeJson, decodeJson, (.?), jsonParser) -import Data.Either (Either(..)) -import Data.Maybe (maybe) -import Data.Path.Pathy (AbsPath, AbsFile, AbsDir, Sandboxed, ()) -import Data.Path.Pathy as Pt +import Data.Either (Either, note) +import Data.Maybe (Maybe) import Data.StrMap (StrMap) import Data.Traversable (traverse) +import Pathy (class IsDirOrFile, class IsRelOrAbs, AbsDir, AbsFile, Path, parseAbsDir, parseAbsFile, posixParser, posixPrinter, printPath, sandboxAny) + +printQPath ∷ ∀ a b. IsRelOrAbs a ⇒ IsDirOrFile b ⇒ Path a b → String +printQPath = sandboxAny >>> printPath posixPrinter + +parseQFilePath ∷ String → Maybe AbsFile +parseQFilePath = parseAbsFile posixParser -type AnyPath = AbsPath Sandboxed -type DirPath = AbsDir Sandboxed -type FilePath = AbsFile Sandboxed +parseQDirPath ∷ String → Maybe AbsDir +parseQDirPath = parseAbsDir posixParser type Vars = StrMap String type Pagination = { offset ∷ Int, limit ∷ Int } type CompileResultR = - { inputs ∷ Array FilePath + { inputs ∷ Array AbsFile , physicalPlan ∷ String } @@ -54,12 +57,8 @@ instance decodeJsonCompileResult ∷ DecodeJson CompileResult where <*> ((obj .? "physicalPlan") <|> pure "") <#> CompileResult -parseFile ∷ String → Either String (Pt.AbsFile Pt.Sandboxed) -parseFile pt = - Pt.parseAbsFile pt - >>= Pt.sandbox Pt.rootDir - <#> (Pt.rootDir _) - # maybe (Left "Incorrect resource") pure +parseFile ∷ String → Either String AbsFile +parseFile = parseQFilePath >>> note "Could not parse resource" compileResultFromString ∷ String → Either String CompileResultR compileResultFromString s = diff --git a/src/Quasar/URI.purs b/src/Quasar/URI.purs new file mode 100644 index 0000000..3f6e897 --- /dev/null +++ b/src/Quasar/URI.purs @@ -0,0 +1,342 @@ +{- +Copyright 2017 SlamData, Inc. + +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 Quasar.URI + ( QAbsoluteURI + , qAbsoluteURI + , MongoURI + , mongoURI + , QRelativeRef + , qRelativeRef + , QURIRef + , qURIRef + , QURI + , qURI + , QHierarchicalPart + , QRelativePart + , QURIHost' + , QURIHost + , QURIHosts + , QQuery + , AbsPath + , AnyPath + , QAuthority + , parseQuery + , printQuery + , parseUserInfo + , printUserInfo + , parseHost + , printHost + , parseHosts + , printHosts + , parsePath + , printPath + , parseHierPath + , printHierPath + , parseFragment + , printFragment + , printRelPath + , parseRelPath + , opts + , module URI + ) where + +import Prelude + +import Data.Array (fromFoldable) +import Data.Bifunctor (bimap, lmap) +import Data.Bitraversable (bitraverse) +import Data.Codec (BasicCodec, basicCodec) +import Data.Either (Either(..), either, note) +import Data.List (List(..), reverse) +import Data.Maybe (Maybe(..), maybe) +import Data.Newtype (un) +import Data.Record.Builder as Builder +import Data.String.NonEmpty (NonEmptyString) +import Data.String.NonEmpty as NES +import Data.These (These) +import Data.Tuple (Tuple(..)) +import Partial.Unsafe (unsafeCrashWith) +import Pathy (Name(..), foldPath, posixParser) +import Pathy as Py +import Text.Parsing.Parser (ParseError, Parser, runParser) +import Type.Row (class RowListNub, class RowToList) +import URI (PathAbsolute, PathRootless) +import URI (URI(..), RelativePart(..), Authority(..), AbsoluteURI(..), HierarchicalPart(..), HierPath, Host(..), Path(..), Port, RelativeRef(..), URIRef, Fragment, Query, UserInfo) as URI +import URI.AbsoluteURI (AbsoluteURIOptions) as URI +import URI.AbsoluteURI (print, parser) as AbsoluteURI +import URI.Common (URIPartParseError(..)) +import URI.Extra.MultiHostPortPair (MultiHostPortPair) as URI +import URI.Extra.MultiHostPortPair (print, parser) as MultiHostPortPair +import URI.Extra.QueryPairs (print, parse, keyToString, valueToString, keyFromString, valueFromString) as QueryPairs +import URI.Extra.QueryPairs (QueryPairs(..), Key, Value) as URI +import URI.Extra.UserPassInfo (print, parse) as UserPassInfo +import URI.Extra.UserPassInfo (UserPassInfo(..)) as URI +import URI.HostPortPair (HostPortPair) as URI +import URI.HostPortPair (print, parser) as HostPortPair +import URI.Path (Path) +import URI.Path (print) as Path +import URI.Path.Absolute (print, PathAbsolute(..)) as PathAbsolute +import URI.Path.NoScheme (print, PathNoScheme(..)) as PathNoScheme +import URI.Path.Rootless (print) as PathRootless +import URI.Path.Segment (PathSegment, PathSegmentNZ, segmentFromString, unsafeSegmentNZFromString, unsafeSegmentNZNCFromString) +import URI.RelativeRef (print, parser, RelPath) as RelativeRef +import URI.RelativeRef (RelativeRefOptions) as URI +import URI.Scheme (Scheme) as URI +import URI.URIRef (print, parser) as URIRef +import URI.URIRef (URIRefOptions) as URI +import URI.URI (print, parser) as URI' +import URI.URI (URIOptions) as URI + +type AbsPath = Py.AbsPath +type AnyPath = Either Py.AbsPath Py.RelPath + +type QURIHost' = These URI.Host URI.Port +-- NOTE: this is same as `URI.HostPortPair URI.Host URI.Port` +type QURIHost = Maybe QURIHost' +-- NOTE: this is same as `URI.MultiHostPortPair URI.Host URI.Port` +type QURIHosts = Array QURIHost' + +type QAuthority = URI.Authority URI.UserPassInfo QURIHost +type QQuery = URI.QueryPairs String String + +type QHierarchicalPart = URI.HierarchicalPart URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath +type QRelativePart = URI.RelativePart URI.UserPassInfo QURIHost (Maybe AbsPath) AnyPath + +type QAbsoluteURI = URI.AbsoluteURI URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath QQuery +type QAbsoluteURIOptions = URI.AbsoluteURIOptions URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath QQuery + +type MongoURI = URI.AbsoluteURI URI.UserPassInfo QURIHosts (Maybe AbsPath) AbsPath QQuery +type MongoURIOptions = URI.AbsoluteURIOptions URI.UserPassInfo QURIHosts (Maybe AbsPath) AbsPath QQuery + +type QRelativeRef = URI.RelativeRef URI.UserPassInfo QURIHost (Maybe AbsPath) AnyPath QQuery URI.Fragment +type QRelativeRefOptions = URI.RelativeRefOptions URI.UserPassInfo QURIHost (Maybe AbsPath) AnyPath QQuery URI.Fragment + +type QURIRef = URI.URIRef URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath AnyPath QQuery URI.Fragment +type QURIRefOptions = URI.URIRefOptions URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath AnyPath QQuery URI.Fragment + +type QURI = URI.URI URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath QQuery URI.Fragment +type QURIOptions = URI.URIOptions URI.UserPassInfo QURIHost (Maybe AbsPath) AbsPath QQuery URI.Fragment + +qAbsoluteURI ∷ BasicCodec (Either ParseError) String QAbsoluteURI +qAbsoluteURI = basicCodec + (flip runParser $ AbsoluteURI.parser opts.absoluteURI) + (AbsoluteURI.print opts.absoluteURI) + +mongoURI ∷ BasicCodec (Either ParseError) String MongoURI +mongoURI = basicCodec + (flip runParser $ AbsoluteURI.parser opts.mongoURI) + (AbsoluteURI.print opts.mongoURI) + +qRelativeRef ∷ BasicCodec (Either ParseError) String QRelativeRef +qRelativeRef = basicCodec + (flip runParser $ RelativeRef.parser opts.relativeRef) + (RelativeRef.print opts.relativeRef) + +qURIRef ∷ BasicCodec (Either ParseError) String QURIRef +qURIRef = basicCodec + (flip runParser $ URIRef.parser opts.uriRef) + (URIRef.print opts.uriRef) + +qURI ∷ BasicCodec (Either ParseError) String QURI +qURI = basicCodec + (flip runParser $ URI'.parser opts.uri) + (URI'.print opts.uri) + +opts ∷ + { absoluteURI ∷ Record QAbsoluteURIOptions + , mongoURI ∷ Record MongoURIOptions + , relativeRef ∷ Record QRelativeRefOptions + , uriRef ∷ Record QURIRefOptions + , uri ∷ Record QURIOptions + } +opts = + { absoluteURI: _common `union` _Host `union` _Path `union` _HierPath + , mongoURI: _common `union` _Hosts `union` _Path `union` _HierPath + , relativeRef: _common `union` _Host `union` _Path`union` _Fragment `union` _RelPath + , uriRef: _common `union` _Host `union` _HierPath `union` _Path `union` _Fragment `union` _RelPath + , uri: _common `union` _Host `union` _HierPath `union` _Path `union` _Fragment + } + where + _common = _UserInfo `union` _Query + + _UserInfo = { parseUserInfo, printUserInfo } + _Host = { parseHosts: parseHost, printHosts: printHost } + _Hosts = { parseHosts, printHosts } + _Query = { parseQuery, printQuery } + _Path = { parsePath, printPath } + _HierPath = { parseHierPath, printHierPath } + _Fragment = { parseFragment, printFragment } + _RelPath = { parseRelPath, printRelPath } + +parseQuery ∷ URI.Query → Either URIPartParseError QQuery +parseQuery = QueryPairs.parse (QueryPairs.keyToString >>> pure) (QueryPairs.valueToString >>> pure) +printQuery ∷ QQuery → URI.Query +printQuery = QueryPairs.print QueryPairs.keyFromString QueryPairs.valueFromString + +parseUserInfo ∷ URI.UserInfo → Either URIPartParseError URI.UserPassInfo +parseUserInfo = UserPassInfo.parse +printUserInfo ∷ URI.UserPassInfo → URI.UserInfo +printUserInfo = UserPassInfo.print + +parseHost ∷ Parser String QURIHost +parseHost = HostPortPair.parser pure pure +printHost ∷ QURIHost → String +printHost = HostPortPair.print id id + +parseHosts ∷ Parser String QURIHosts +parseHosts = MultiHostPortPair.parser pure pure +printHosts ∷ QURIHosts → String +printHosts = MultiHostPortPair.print id id + +parsePath ∷ Path → Either URIPartParseError (Maybe AbsPath) +parsePath = case _ of + URI.Path [] → pure Nothing + p → Just <$> _parseAbsPath (Path.print p) +printPath ∷ Maybe AbsPath → Path +printPath = case _ of + Nothing → URI.Path [] + Just absP → + case bimap viewAbsDir viewAbsFile absP of + Left d → + URI.Path + $ (fromFoldable d <#> runName >>> segmentFromString) <> [ forceTrailingSlash ] + Right (Tuple d n) → + URI.Path + $ (fromFoldable d <#> asSegment) <> [asSegment n] + +parseHierPath ∷ Either PathAbsolute PathRootless → Either URIPartParseError AbsPath +parseHierPath = _parseAbsPath <<< either PathAbsolute.print PathRootless.print +printHierPath ∷ AbsPath → Either PathAbsolute PathRootless +printHierPath = _printAbsPath >>> Left + +parseFragment ∷ URI.Fragment → Either URIPartParseError URI.Fragment +parseFragment = Right +printFragment ∷ URI.Fragment → URI.Fragment +printFragment = id + +printRelPath ∷ AnyPath → RelativeRef.RelPath +printRelPath = bimap _printAbsPath _printRelPath +parseRelPath ∷ RelativeRef.RelPath → Either URIPartParseError AnyPath +parseRelPath = bitraverse + (PathAbsolute.print >>> _parseAbsPath) + (PathNoScheme.print >>> _parseRelPath) + +-- ===== INTERNAL ===== + +_printAbsPath ∷ Py.AbsPath → PathAbsolute +_printAbsPath = bimap viewAbsDir viewAbsFile >>> case _ of + Left Nil → PathAbsolute.PathAbsolute Nothing + Left (Cons head tail) → PathAbsolute.PathAbsolute $ Just + $ Tuple (asSegmentNZ head) + $ (asSegment <$> fromFoldable tail) <> [ forceTrailingSlash ] + Right (Tuple d n) → case d of + Nil → PathAbsolute.PathAbsolute $ Just $ Tuple (asSegmentNZ n) [] + Cons head tail → PathAbsolute.PathAbsolute + $ Just + $ Tuple (asSegmentNZ head) + $ (asSegment <$> fromFoldable tail) <> [ asSegment n ] + +_printRelPath ∷ Py.RelPath → PathNoScheme.PathNoScheme +_printRelPath = bimap viewRelDir viewRelFile >>> case _ of + Left Nil → PathNoScheme.PathNoScheme $ Tuple (unsafeSegmentNZNCFromString currentDirSegment) [] + Left (Cons head tail) → + PathNoScheme.PathNoScheme + $ Tuple (unsafeSegmentNZNCFromString $ maybe parentDirSegment (un Name) head) + $ (segmentFromString <<< maybe ".." runName <$> fromFoldable tail) <> [ forceTrailingSlash ] + + Right (Tuple d n) → case d of + Nil → PathNoScheme.PathNoScheme $ Tuple (unsafeSegmentNZNCFromString $ un Name n) [] + Cons head tail → PathNoScheme.PathNoScheme + $ Tuple (unsafeSegmentNZNCFromString $ maybe parentDirSegment (un Name) head) + $ (segmentFromString <<< maybe ".." runName <$> fromFoldable tail) <> [ asSegment n ] + + +-- Array of segments is joined using "/" so to have trailing slash in rendered +-- string for dir pathes, we need to use this empty segment. +forceTrailingSlash ∷ PathSegment +forceTrailingSlash = segmentFromString "" + +currentDirSegment ∷ NonEmptyString +currentDirSegment = case NES.fromString "." of + Nothing → unsafeCrashWith "unreachable case in currentDirSegment" + Just a → a + +parentDirSegment ∷ NonEmptyString +parentDirSegment = case NES.fromString ".." of + Nothing → unsafeCrashWith "unreachable case in parentDirSegment" + Just a → a + +_parseAbsPath ∷ String → Either URIPartParseError Py.AbsPath +_parseAbsPath = + Py.parsePath posixParser + (const Nothing) + (Just <<< Left) + (const Nothing) + (Just <<< Right) + Nothing + >>> note (URIPartParseError "Could not parse valid absolute path") + +_parseRelPath ∷ String → Either URIPartParseError Py.RelPath +_parseRelPath = + Py.parsePath posixParser + (Just <<< Left) + (const Nothing) + (Just <<< Right) + (const Nothing) + Nothing + >>> note (URIPartParseError "Could not parse valid relative path") + +-- Union which rejects duplicates +union + ∷ ∀ r1 r2 r3 r3l + . Union r1 r2 r3 + ⇒ RowToList r3 r3l + ⇒ RowListNub r3l r3l + ⇒ { | r1 } + → { | r2 } + → { | r3 } +union r1 r2 = Builder.build (Builder.merge r2) r1 + +asSegmentNZ ∷ ∀ a. Py.Name a → PathSegmentNZ +asSegmentNZ = un Py.Name >>> unsafeSegmentNZFromString + +asSegment ∷ ∀ a. Py.Name a → PathSegment +asSegment = runName >>> segmentFromString + +runName ∷ ∀ a. Py.Name a → String +runName = un Py.Name >>> NES.toString + +viewAbsDir ∷ Py.Path Py.Abs Py.Dir → List (Py.Name Py.Dir) +viewAbsDir = reverse <<< go + where + go p = foldPath Nil + (\_ → unsafeCrashWith "ParentOf node in viewDir") + (flip Cons <<< go) p + +viewAbsFile ∷ Py.Path Py.Abs Py.File → Tuple (List (Py.Name Py.Dir)) (Py.Name Py.File) +viewAbsFile = Py.peelFile >>> lmap viewAbsDir + + +viewRelDir ∷ Py.Path Py.Rel Py.Dir → List (Maybe (Py.Name Py.Dir)) +viewRelDir = reverse <<< go + where + go p' = foldPath Nil + (\p → Cons Nothing (go p)) + (\p n → Cons (Just n) (go p)) p' + +viewRelFile ∷ Py.Path Py.Rel Py.File → Tuple (List (Maybe (Py.Name Py.Dir))) (Py.Name Py.File) +viewRelFile = Py.peelFile >>> lmap viewRelDir diff --git a/test/src/Test/Implementation.purs b/test/src/Test/Implementation.purs index b1dd6e3..d264f1a 100644 --- a/test/src/Test/Implementation.purs +++ b/test/src/Test/Implementation.purs @@ -23,7 +23,7 @@ import Test.Property.Main as Prop import Test.QuickCheck (QC) import Test.Unit.Main as Unit -main ∷ forall eff. QC (assert ∷ ASSERT | eff) Unit +main ∷ ∀ eff. QC (assert ∷ ASSERT | eff) Unit main = do Prop.main Unit.main diff --git a/test/src/Test/Main.purs b/test/src/Test/Main.purs index 2b817e4..c6c3966 100644 --- a/test/src/Test/Main.purs +++ b/test/src/Test/Main.purs @@ -34,12 +34,16 @@ import Data.Either (Either(..), isRight) import Data.Foldable (traverse_) import Data.Functor.Coproduct (left) import Data.Maybe (Maybe(..)) -import Data.Path.Pathy (rootDir, dir, file, ()) import Data.Posix.Signal (Signal(SIGTERM)) import Data.StrMap as SM import Data.String as Str +import Data.String.NonEmpty as NES +import Data.Symbol (SProxy(..)) +import Data.These (These(..)) import Data.Tuple (Tuple(..)) -import Data.URI as URI +import URI.Host.RegName as RegName +import URI.Port as Port +import URI.Scheme as Scheme import Network.HTTP.Affjax (AJAX) import Node.ChildProcess as CP import Node.Encoding (Encoding(..)) @@ -48,9 +52,11 @@ import Node.Process (PROCESS) import Node.Process as Proc import Partial (crashWith) import Partial.Unsafe (unsafePartial) +import Pathy (rootDir, dir, file, ()) import Quasar.Advanced.QuasarAF.Interpreter.Aff (Config, eval) import Quasar.Data (QData(..)) import Quasar.Data.Json as Json +import Quasar.URI as URI import Quasar.Mount (MountConfig(..)) import Quasar.QuasarF (QuasarF, QError(..)) import Quasar.QuasarF as QF @@ -76,8 +82,13 @@ run pred qf = do config ∷ Config () config = { basePath: Left - { scheme: URI.Scheme "http" - , authority: Just (URI.Authority Nothing [Tuple (URI.NameAddress "localhost") (Just (URI.Port 53174))]) + { scheme: Scheme.unsafeFromString "http" + , authority: Just + $ URI.Authority Nothing + $ Just + $ Both + (URI.NameAddress $ RegName.unsafeFromString $ unsafePartial $ NES.unsafeFromString "localhost") + (Port.unsafeFromInt 53174) , path: rootDir } , idToken: Nothing @@ -194,7 +205,7 @@ main = void $ runAff (const (pure unit)) $ jumpOutOnError do log "\nDeleteMount:" run isRight $ QF.deleteMount (Right testMount2) - log "\nInvokeFile:" + log "\nInvokeFile" run isRight $ QF.createMount (Left testMount3) mountConfig3 run isRight $ QF.invokeFile Json.Precise testProcess (SM.fromFoldable [Tuple "a" "4", Tuple "b" "2"]) Nothing @@ -209,16 +220,16 @@ main = void $ runAff (const (pure unit)) $ jumpOutOnError do where testDbAnyDir = rootDir - nonexistant = rootDir file "nonexistant" - testFile1 = rootDir file "test1" - testFile2Dir = rootDir dir "subdir" - testFile2 = testFile2Dir file "test2" - testFile3Dir = rootDir dir "what" - testFile3 = testFile3Dir file "test2" - testMount = rootDir file "testMount" - testMount2 = rootDir file "testMount2" - testMount3 = rootDir dir "testMount3" dir "" - testProcess = rootDir dir "testMount3" file "test" + nonexistant = rootDir file (SProxy ∷ SProxy "nonexistant") + testFile1 = rootDir file (SProxy ∷ SProxy "test1") + testFile2Dir = rootDir dir (SProxy ∷ SProxy "subdir") + testFile2 = testFile2Dir file (SProxy ∷ SProxy "test2") + testFile3Dir = rootDir dir (SProxy ∷ SProxy "what") + testFile3 = testFile3Dir file (SProxy ∷ SProxy "test2") + testMount = rootDir file (SProxy ∷ SProxy "testMount") + testMount2 = rootDir file (SProxy ∷ SProxy "testMount2") + testMount3 = rootDir dir (SProxy ∷ SProxy "testMount3") + testProcess = rootDir dir (SProxy ∷ SProxy "testMount3") file (SProxy ∷ SProxy "test") isNotFound ∷ ∀ a. Either QError a → Boolean isNotFound e = case e of diff --git a/test/src/Test/Unit/Main.purs b/test/src/Test/Unit/Main.purs index abe6e6c..20b6831 100644 --- a/test/src/Test/Unit/Main.purs +++ b/test/src/Test/Unit/Main.purs @@ -21,14 +21,17 @@ import Prelude import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) import Data.Argonaut.Parser as JP +import Data.Codec (decode, encode) import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.Monoid (mempty) -import Data.NonEmpty as NE +import Data.String.NonEmpty as NES +import Data.These (These(..)) import Data.Time.Duration (Seconds(..)) -import Data.Tuple (Tuple(..)) -import Data.URI as URI -import Data.URI.AbsoluteURI as AbsoluteURI +import URI.Host.RegName as RegName +import URI.Port as Port +import Partial.Unsafe (unsafePartial) +import Quasar.URI as URI import Quasar.Mount as QM import Quasar.Mount.Couchbase as CB import Quasar.Mount.MongoDB as Mongo @@ -51,7 +54,7 @@ main = do testURIParse (map CBT.TestConfig <$> CB.fromURI) "couchbase://localhost/testBucket?password=&docTypeKey=" (CBT.TestConfig - { host: Tuple (URI.NameAddress "localhost") Nothing + { host: This (URI.NameAddress $ RegName.unsafeFromString $ unsafePartial $ NES.unsafeFromString "localhost") , bucketName: "testBucket" , password: "" , docTypeKey: "" @@ -59,18 +62,18 @@ main = do }) testURIParse (map CBT.TestConfig <$> CB.fromURI) - "couchbase://localhost:99999/testBucket?password=pass&docTypeKey=type&queryTimeoutSeconds=20" + "couchbase://localhost:9999/testBucket?password=pass&docTypeKey=type&queryTimeoutSeconds=20" (CBT.TestConfig - { host: Tuple (URI.NameAddress "localhost") (Just (URI.Port 99999)) + { host: Both (URI.NameAddress $ RegName.unsafeFromString $ unsafePartial $ NES.unsafeFromString "localhost") (Port.unsafeFromInt 9999) , bucketName: "testBucket" , password: "pass" , docTypeKey: "type" , queryTimeout: Just (Seconds (20.0)) }) let mongoURI = - AbsoluteURI.print + encode URI.mongoURI (Mongo.toURI - { hosts: NE.singleton (Tuple (URI.NameAddress "localhost") (Just (URI.Port 12345))) + { hosts: [Both (URI.NameAddress $ RegName.unsafeFromString $ unsafePartial $ NES.unsafeFromString "localhost") (Port.unsafeFromInt 12345)] , auth: Nothing , props: mempty}) if mongoURI == "mongodb://localhost:12345/" @@ -81,12 +84,12 @@ testURIParse ∷ ∀ a eff . Eq a ⇒ Show a - ⇒ (URI.AbsoluteURI → Either String a) + ⇒ (URI.QAbsoluteURI → Either String a) → String → a - → Eff (assert :: ASSERT | eff) Unit + → Eff (assert ∷ ASSERT | eff) Unit testURIParse fromURI uri expected = - case AbsoluteURI.parse uri of + case decode URI.qAbsoluteURI uri of Left err → fail $ "Test URI failed to parse as a URI even: \n\n\t" <> uri <> "\n\n\t" <> show err <> "\n\n" Right auri → case fromURI auri of @@ -95,5 +98,5 @@ testURIParse fromURI uri expected = | config == expected → pure unit | otherwise → fail $ "Test URI failed to parse as expected config: \n\n\t" <> uri <> "\n\n\tExpected: " <> show expected <> "\n\n\tActual: " <> show config <> "\n\n" -fail ∷ ∀ eff. String → Eff (assert :: ASSERT | eff) Unit +fail ∷ ∀ eff. String → Eff (assert ∷ ASSERT | eff) Unit fail = flip assert' false diff --git a/test/src/Util/Effect.purs b/test/src/Util/Effect.purs index 957b6fa..603a0e4 100644 --- a/test/src/Util/Effect.purs +++ b/test/src/Util/Effect.purs @@ -14,14 +14,13 @@ import Node.Process (PROCESS) import Test.Assert (ASSERT) type Effects = - ( avar :: AVAR - , cp :: CHILD_PROCESS - , process :: PROCESS - , exception :: EXCEPTION - , fs :: FS - , buffer :: BUFFER - , console :: CONSOLE - , ajax :: AJAX - , assert :: ASSERT + ( avar ∷ AVAR + , cp ∷ CHILD_PROCESS + , process ∷ PROCESS + , exception ∷ EXCEPTION + , fs ∷ FS + , buffer ∷ BUFFER + , console ∷ CONSOLE + , ajax ∷ AJAX + , assert ∷ ASSERT ) -