Prechádzať zdrojové kódy

Add high level Output and API error types

jherve 1 rok pred
rodič
commit
ebfa46be64

+ 30 - 13
src/LinkedIn.purs

@@ -1,14 +1,17 @@
-module LinkedIn (encodeToJson, getContext, extractFromDocument, extractFromDocumentInContext) where
+module LinkedIn (APIError(..), encodeToJson, getContext, extractFromDocument, extractFromDocumentInContext) where
 
 import Prelude
 
-import Control.Monad.Except (ExceptT, lift, runExceptT, throwError)
+import Control.Monad.Except (ExceptT, lift, runExceptT, throwError, withExceptT)
 import Data.Argonaut.Core (Json)
-import Data.Argonaut.Encode (encodeJson)
+import Data.Argonaut.Encode (class EncodeJson, encodeJson)
+import Data.Argonaut.Encode.Generic (genericEncodeJson)
 import Data.Either (Either(..))
+import Data.Generic.Rep (class Generic)
 import Data.Maybe (Maybe(..))
+import Data.Show.Generic (genericShow)
 import Effect (Effect)
-import LinkedIn.Output (toOutput)
+import LinkedIn.Output (OutputError, toOutput)
 import LinkedIn.Output.Types (Output)
 import LinkedIn.PageUrl (PageUrl, pageUrlP)
 import Parsing (runParser)
@@ -16,28 +19,42 @@ import Web.DOM (Document)
 import Web.DOM.Document (url)
 import Web.URL as U
 
-getContext ∷ Document → Effect (Either String PageUrl)
+data APIError =
+  ErrorExtraction OutputError
+  | ErrorInvalidUrl
+  | ErrorUnexpectedUrl
+
+derive instance Generic APIError _
+instance Show APIError where
+  show = genericShow
+instance EncodeJson APIError where
+  encodeJson a = genericEncodeJson a
+
+getContext ∷ Document → Effect (Either APIError PageUrl)
 getContext = runExceptT <<< getContext'
 
-getContext' ∷ Document → ExceptT String Effect PageUrl
+getContext' ∷ Document → ExceptT APIError Effect PageUrl
 getContext' dom = do
   u <- lift $ url dom
   case U.fromAbsolute u of
-    Nothing -> throwError "No URL found"
+    Nothing -> throwError ErrorInvalidUrl
     Just u' ->  case runParser (U.pathname u') pageUrlP of
-      Left _ -> throwError "Unexpected URL"
+      Left _ -> throwError ErrorUnexpectedUrl
       Right page -> pure page
 
-extractFromDocument :: Document -> Effect (Either String Output)
+extractFromDocument :: Document -> Effect (Either APIError Output)
 extractFromDocument = runExceptT <<< extractFromDocument'
 
-extractFromDocument' ∷ Document → ExceptT String Effect Output
+extractFromDocument' ∷ Document → ExceptT APIError Effect Output
 extractFromDocument' dom = do
   ctx <- getContext' dom
-  toOutput ctx dom
+  toOutput' ctx dom
+
+extractFromDocumentInContext :: PageUrl -> Document -> Effect (Either APIError Output)
+extractFromDocumentInContext url dom = runExceptT $ toOutput' url dom
 
-extractFromDocumentInContext :: PageUrl -> Document -> Effect (Either String Output)
-extractFromDocumentInContext url dom = runExceptT $ toOutput url dom
+toOutput' ∷ PageUrl → Document → ExceptT APIError Effect Output
+toOutput' ctx dom = withExceptT (\err -> ErrorExtraction err) $ toOutput ctx dom
 
 encodeToJson :: Either String Output -> Json
 encodeToJson = encodeJson

+ 10 - 10
src/LinkedIn/Output.purs

@@ -1,15 +1,14 @@
-module LinkedIn.Output (run, runToDetached, toOutput) where
+module LinkedIn.Output (module LinkedIn.Output.Types, run, runToDetached, toOutput) where
 
 import Prelude
 
-import Control.Monad.Except (ExceptT, except, lift, withExceptT)
-import Data.Either (Either(..))
+import Control.Monad.Except (ExceptT, except, lift, throwError, withExceptT)
 import Data.Traversable (class Traversable, traverse)
 import Effect (Effect)
 import LinkedIn.DetachedNode (DetachedNode, toDetached)
 import LinkedIn.Extractible (class Extractible)
 import LinkedIn.Extractible as LE
-import LinkedIn.Output.Types (Output)
+import LinkedIn.Output.Types (Output, OutputError(..))
 import LinkedIn.Page.JobOffer (JobOfferPage)
 import LinkedIn.Page.Projects (ProjectsPage)
 import LinkedIn.Page.Skills (SkillsPage)
@@ -17,6 +16,7 @@ import LinkedIn.Page.WorkExperiences (WorkExperiencesPage)
 import LinkedIn.PageUrl (PageUrl(..))
 import LinkedIn.QueryRunner (QueryError)
 import LinkedIn.UI.Elements.Parser (toUIElement)
+import Parsing (parseErrorMessage)
 import Type.Proxy (Proxy(..))
 import Web.DOM (Document)
 
@@ -25,11 +25,11 @@ run :: forall t.
   => Extractible t
   => Proxy t
   -> Document
-  -> ExceptT String Effect Output
+  -> ExceptT OutputError Effect Output
 run prox dom = do
-  detached <- withExceptT (\_ -> "Error on detach") $ runToDetached prox dom
-  asUI <- withExceptT (\_ -> "error on conversion to UI element") $ except $ traverse toUIElement detached
-  except $ LE.extract asUI
+  detached <- withExceptT (\err -> ErrorOnDetach err) $ runToDetached prox dom
+  asUI <- withExceptT (\err -> ErrorOnUIConversion $ parseErrorMessage err) $ except $ traverse toUIElement detached
+  withExceptT (\err -> ErrorOnExtract err) $ except $ LE.extract asUI
 
 runToDetached :: forall t.
   Traversable t
@@ -41,10 +41,10 @@ runToDetached _ dom = do
   qRes <- LE.query @t dom
   lift $ traverse toDetached qRes
 
-toOutput ∷ PageUrl → (Document → ExceptT String Effect Output)
+toOutput ∷ PageUrl → (Document → ExceptT OutputError Effect Output)
 toOutput = case _ of
   UrlProjects _ -> run (Proxy :: Proxy ProjectsPage)
   UrlSkills _ -> run (Proxy :: Proxy SkillsPage)
   UrlWorkExperience _ -> run (Proxy :: Proxy WorkExperiencesPage)
   UrlJobOffer _ -> run (Proxy :: Proxy JobOfferPage)
-  _ -> const $ except $ Left "Not handled yet"
+  u -> const $ throwError $ ErrorUrlNotSupported u

+ 15 - 0
src/LinkedIn/Output/Types.purs

@@ -8,9 +8,11 @@ import Data.Generic.Rep (class Generic)
 import Data.List.Types (NonEmptyList)
 import Data.Show.Generic (genericShow)
 import LinkedIn.Jobs.JobOffer as JO
+import LinkedIn.PageUrl (PageUrl)
 import LinkedIn.Profile.Project (Project)
 import LinkedIn.Profile.Skill (Skill)
 import LinkedIn.Profile.WorkExperience (WorkExperience)
+import LinkedIn.QueryRunner (QueryError)
 
 data Output =
   OutProjects (NonEmptyList Project)
@@ -23,3 +25,16 @@ instance Show Output where
   show = genericShow
 instance EncodeJson Output where
   encodeJson a = genericEncodeJson a
+
+
+data OutputError =
+  ErrorOnDetach QueryError
+  | ErrorOnUIConversion String
+  | ErrorOnExtract String
+  | ErrorUrlNotSupported PageUrl
+
+derive instance Generic OutputError _
+instance Show OutputError where
+  show = genericShow
+instance EncodeJson OutputError where
+  encodeJson a = genericEncodeJson a

+ 4 - 0
src/LinkedIn/PageUrl.purs

@@ -3,6 +3,8 @@ module LinkedIn.PageUrl (PageUrl(..), pageUrlP) where
 import Prelude
 
 import Control.Alt ((<|>))
+import Data.Argonaut.Encode (class EncodeJson)
+import Data.Argonaut.Encode.Generic (genericEncodeJson)
 import Data.Generic.Rep (class Generic)
 import Data.Int64 as I64
 import Data.Maybe (Maybe(..))
@@ -27,6 +29,8 @@ derive instance Eq PageUrl
 derive instance Generic PageUrl _
 instance Show PageUrl where
   show = genericShow
+instance EncodeJson PageUrl where
+  encodeJson a = genericEncodeJson a
 
 pathComponentP :: String -> Parser String Unit
 pathComponentP s = do

+ 4 - 0
src/LinkedIn/QueryRunner.purs

@@ -4,6 +4,8 @@ import Prelude
 
 import Control.Alt ((<|>))
 import Control.Monad.Except (ExceptT(..), mapExceptT, runExceptT, throwError)
+import Data.Argonaut.Encode (class EncodeJson)
+import Data.Argonaut.Encode.Generic (genericEncodeJson)
 import Data.Array as A
 import Data.Either (Either(..), note)
 import Data.Generic.Rep (class Generic)
@@ -22,6 +24,8 @@ data QueryError =
   | QNodeUnexpectedType String String
   | QTextNotFoundError
   | QChooseError
+instance EncodeJson QueryError where
+  encodeJson a = genericEncodeJson a
 
 derive instance Generic QueryError _
 derive instance Eq QueryError

+ 4 - 1
src/LinkedIn/UI/Basic/Types.purs

@@ -3,11 +3,12 @@ module LinkedIn.UI.Basic.Types where
 import Prelude
 
 import Data.Argonaut.Encode (class EncodeJson)
-import Data.Argonaut.Encode.Encoders (encodeString)
+import Data.Argonaut.Encode.Encoders (encodeNumber, encodeString)
 import Data.Argonaut.Encode.Generic (genericEncodeJson)
 import Data.Date (Month, Year)
 import Data.Generic.Rep (class Generic)
 import Data.Int64 (Int64)
+import Data.Int64 as I64
 import Data.Show.Generic (genericShow)
 
 newtype JobOfferId = JobOfferId Int64
@@ -16,6 +17,8 @@ derive instance Eq JobOfferId
 derive instance Generic JobOfferId _
 instance Show JobOfferId where
   show = genericShow
+instance EncodeJson JobOfferId where
+  encodeJson (JobOfferId a) = encodeNumber $ I64.toNumber a
 
 data MonthYear = MonthYear Month Year