notion-client-0.6.1.0: src/Notion/V1/FileUploads.hs
-- | @\/v1\/file_uploads@
module Notion.V1.FileUploads
( -- * Main types
FileUploadID,
FileUploadObject (..),
FileUploadStatus (..),
CreateFileUpload (..),
SendFileUpload (..),
-- * Supporting types
NumberOfParts (..),
FileImportResult (..),
-- * Smart constructors
mkSinglePartUpload,
mkMultiPartUpload,
mkExternalUrlUpload,
mkSendFileUpload,
-- * Servant
API,
)
where
import Data.Aeson ((.:), (.:?), (.=))
import Data.Aeson qualified as Aeson
import Notion.Prelude
import Notion.V1.Common (UUID)
import Notion.V1.ListOf (ListOf)
import Prelude hiding (id)
-- | File upload ID
type FileUploadID = UUID
-- | Status of a file upload
data FileUploadStatus
= Pending
| Uploaded
| Expired
| Failed
deriving stock (Eq, Show)
instance FromJSON FileUploadStatus where
parseJSON = \case
String "pending" -> pure Pending
String "uploaded" -> pure Uploaded
String "expired" -> pure Expired
String "failed" -> pure Failed
String other -> fail $ "Unknown FileUploadStatus: " <> unpack other
_ -> fail "Expected string for FileUploadStatus"
instance ToJSON FileUploadStatus where
toJSON = \case
Pending -> String "pending"
Uploaded -> String "uploaded"
Expired -> String "expired"
Failed -> String "failed"
instance ToHttpApiData FileUploadStatus where
toQueryParam = \case
Pending -> "pending"
Uploaded -> "uploaded"
Expired -> "expired"
Failed -> "failed"
-- | Number of parts for multi-part uploads
data NumberOfParts = NumberOfParts
{ total :: Natural,
sent :: Natural
}
deriving stock (Eq, Generic, Show)
instance FromJSON NumberOfParts where
parseJSON = genericParseJSON aesonOptions
instance ToJSON NumberOfParts where
toJSON = genericToJSON aesonOptions
-- | Result of a file import operation
data FileImportResult
= FileImportSuccess
{ importedTime :: POSIXTime
}
| FileImportError
{ importedTime :: POSIXTime,
errorType :: Text,
errorCode :: Text,
errorMessage :: Text,
errorParameter :: Maybe Text,
errorStatusCode :: Maybe Int
}
deriving stock (Show)
instance FromJSON FileImportResult where
parseJSON = \case
Object o -> do
resultType <- o .: "type"
importedTimeStr <- o .: "imported_time"
importedTime <- parseISO8601 importedTimeStr
case resultType of
"success" -> pure FileImportSuccess {..}
"error" -> do
errObj <- o .: "error"
errorType <- errObj .: "type"
errorCode <- errObj .: "code"
errorMessage <- errObj .: "message"
errorParameter <- errObj .:? "parameter"
errorStatusCode <- errObj .:? "status_code"
pure FileImportError {..}
other -> fail $ "Unknown FileImportResult type: " <> unpack (other :: Text)
_ -> fail "Expected object for FileImportResult"
instance ToJSON FileImportResult where
toJSON FileImportSuccess {..} =
Aeson.object
[ "type" .= ("success" :: Text),
"imported_time" .= posixToISO8601 importedTime,
"success" .= Aeson.object []
]
toJSON FileImportError {..} =
Aeson.object
[ "type" .= ("error" :: Text),
"imported_time" .= posixToISO8601 importedTime,
"error"
.= Aeson.object
( [ "type" .= errorType,
"code" .= errorCode,
"message" .= errorMessage
]
<> maybe [] (\p -> ["parameter" .= p]) errorParameter
<> maybe [] (\s -> ["status_code" .= s]) errorStatusCode
)
]
-- | Notion file upload object
data FileUploadObject = FileUploadObject
{ id :: FileUploadID,
object :: Text,
status :: FileUploadStatus,
filename :: Maybe Text,
contentType :: Maybe Text,
contentLength :: Maybe Natural,
createdTime :: POSIXTime,
lastEditedTime :: POSIXTime,
createdBy :: Value,
inTrash :: Bool,
expiryTime :: Maybe POSIXTime,
numberOfParts :: Maybe NumberOfParts,
fileImportResult :: Maybe FileImportResult
}
deriving stock (Generic, Show)
instance FromJSON FileUploadObject where
parseJSON = \case
Object o -> do
id <- o .: "id"
object <- o .: "object"
status <- o .: "status"
filename <- o .:? "filename"
contentType <- o .:? "content_type"
contentLength <- o .:? "content_length"
createdTimeStr <- o .: "created_time"
createdTime <- parseISO8601 createdTimeStr
lastEditedTimeStr <- o .: "last_edited_time"
lastEditedTime <- parseISO8601 lastEditedTimeStr
createdBy <- o .: "created_by"
inTrash <- o .: "in_trash"
mExpiryStr <- o .:? "expiry_time"
expiryTime <- case mExpiryStr of
Nothing -> pure Nothing
Just str -> Just <$> parseISO8601 str
numberOfParts <- o .:? "number_of_parts"
fileImportResult <- o .:? "file_import_result"
pure FileUploadObject {..}
_ -> fail "Expected object for FileUploadObject"
instance ToJSON FileUploadObject where
toJSON FileUploadObject {..} =
Aeson.object $
[ "id" .= id,
"object" .= object,
"status" .= status,
"created_time" .= posixToISO8601 createdTime,
"last_edited_time" .= posixToISO8601 lastEditedTime,
"created_by" .= createdBy,
"in_trash" .= inTrash
]
<> maybe [] (\f -> ["filename" .= f]) filename
<> maybe [] (\ct -> ["content_type" .= ct]) contentType
<> maybe [] (\cl -> ["content_length" .= cl]) contentLength
<> maybe [] (\et -> ["expiry_time" .= posixToISO8601 et]) expiryTime
<> maybe [] (\np -> ["number_of_parts" .= np]) numberOfParts
<> maybe [] (\fir -> ["file_import_result" .= fir]) fileImportResult
-- | Request body for creating a file upload
data CreateFileUpload = CreateFileUpload
{ mode :: Maybe Text,
filename :: Maybe Text,
contentType :: Maybe Text,
numberOfParts :: Maybe Natural,
externalUrl :: Maybe Text
}
deriving stock (Generic, Show)
instance ToJSON CreateFileUpload where
toJSON = genericToJSON aesonOptions
-- | Data for sending file content via multipart/form-data
data SendFileUpload = SendFileUpload
{ filePath :: FilePath,
fileName :: Text,
fileContentType :: Text,
partNumber :: Maybe Natural
}
deriving stock (Show)
instance ToMultipart Tmp SendFileUpload where
toMultipart SendFileUpload {..} =
MultipartData
{ inputs = maybe [] (\n -> [Input "part_number" (pack (show n))]) partNumber,
files = [FileData "file" fileName fileContentType filePath]
}
-- ---------------------------------------------------------------------------
-- Smart constructors
-- ---------------------------------------------------------------------------
-- | Create a single-part file upload request with an optional filename.
mkSinglePartUpload :: Maybe Text -> CreateFileUpload
mkSinglePartUpload fname =
CreateFileUpload
{ mode = Nothing,
filename = fname,
contentType = Nothing,
numberOfParts = Nothing,
externalUrl = Nothing
}
-- | Create a multi-part file upload request.
mkMultiPartUpload ::
-- | filename (required)
Text ->
-- | number of parts (required)
Natural ->
-- | content type (optional)
Maybe Text ->
CreateFileUpload
mkMultiPartUpload fname parts ct =
CreateFileUpload
{ mode = Just "multi_part",
filename = Just fname,
contentType = ct,
numberOfParts = Just parts,
externalUrl = Nothing
}
-- | Create an external URL file upload request.
mkExternalUrlUpload ::
-- | external URL (required, must be HTTPS)
Text ->
-- | filename (optional)
Maybe Text ->
CreateFileUpload
mkExternalUrlUpload url fname =
CreateFileUpload
{ mode = Just "external_url",
filename = fname,
contentType = Nothing,
numberOfParts = Nothing,
externalUrl = Just url
}
-- | Create a 'SendFileUpload' for a single-part upload.
mkSendFileUpload ::
-- | path to the file on disk
FilePath ->
-- | filename
Text ->
-- | MIME content type (e.g., @"image\/png"@)
Text ->
SendFileUpload
mkSendFileUpload fp fname ct =
SendFileUpload
{ filePath = fp,
fileName = fname,
fileContentType = ct,
partNumber = Nothing
}
-- | Servant API
type API =
"file_uploads"
:> ( ReqBody '[JSON] CreateFileUpload
:> Post '[JSON] FileUploadObject
:<|> Capture "file_upload_id" FileUploadID
:> Get '[JSON] FileUploadObject
:<|> Capture "file_upload_id" FileUploadID
:> "send"
:> MultipartForm Tmp SendFileUpload
:> Post '[JSON] FileUploadObject
:<|> Capture "file_upload_id" FileUploadID
:> "complete"
:> Post '[JSON] FileUploadObject
:<|> QueryParam "status" FileUploadStatus
:> QueryParam "start_cursor" Text
:> QueryParam "page_size" Natural
:> Get '[JSON] (ListOf FileUploadObject)
)