diff --git a/components/api-docs/v1/get-slate.js b/components/api-docs/v1/get-slate.js index c7f5ccf6..abcfef9d 100644 --- a/components/api-docs/v1/get-slate.js +++ b/components/api-docs/v1/get-slate.js @@ -60,7 +60,7 @@ export default class APIDocsGetSlate extends React.Component { return ( diff --git a/components/api-docs/v1/get.js b/components/api-docs/v1/get.js index 83bf38c5..528c4e64 100644 --- a/components/api-docs/v1/get.js +++ b/components/api-docs/v1/get.js @@ -118,7 +118,7 @@ export default class APIDocsGet extends React.Component { return ( diff --git a/components/api-docs/v1/update-slate.js b/components/api-docs/v1/update-slate.js index ceb11e98..b260d46f 100644 --- a/components/api-docs/v1/update-slate.js +++ b/components/api-docs/v1/update-slate.js @@ -60,7 +60,7 @@ export default class APIDocsUpdateSlate extends React.Component { return ( diff --git a/components/api-docs/v1/upload.js b/components/api-docs/v1/upload.js index f3fd0a67..1ab4c497 100644 --- a/components/api-docs/v1/upload.js +++ b/components/api-docs/v1/upload.js @@ -75,7 +75,7 @@ export default class APIDocsUploadToSlate extends React.Component { return ( { + return `const response = await fetch("https://slate.host/api/v2/create-collection", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Basic ${key}", // API key + }, + body: JSON.stringify({ + data: { + name: "My Dog Fido", + isPublic: true, + body: "This is an album of my dog, Fido, a golden retriever", + tags: ["dogs", "retrievers", "golden retriever"] + }, + }), + });`; +}; + +const EXAMPLE_CODE_PY = (key, slateId) => + `import requests + +headers = { + "content-type": "application/json", + "Authorization": "Basic ${key}", # API key +} + +postJson = { + data: { + name: "My Dog Fido", + isPublic: true, + body: "This is an album of my dog, Fido, a golden retriever", + tags: ["dogs", "retrievers", "golden retriever"] + } + } + +url = "https://slate.host/api/v2/create-collection" + +r = requests.post(url, headers=headers, json=postJson)`; + +export default class APIDocsCreateCollection extends React.Component { + render() { + let language = this.props.language; + let key = this.props.APIKey; + let slateId = this.props.slateId; + + let code = { + javascript: EXAMPLE_CODE_JS(key, slateId), + python: EXAMPLE_CODE_PY(key, slateId), + }; + return ( + + + + + ); + } +} diff --git a/components/api-docs/v2/create-link.js b/components/api-docs/v2/create-link.js index 0863f3e8..b0ae6d7f 100644 --- a/components/api-docs/v2/create-link.js +++ b/components/api-docs/v2/create-link.js @@ -51,7 +51,7 @@ export default class APIDocsCreateLink extends React.Component { return ( diff --git a/components/api-docs/v2/get-slate.js b/components/api-docs/v2/get-slate.js index 8ff219ce..91df2c3c 100644 --- a/components/api-docs/v2/get-slate.js +++ b/components/api-docs/v2/get-slate.js @@ -60,7 +60,7 @@ export default class APIDocsGetCollection extends React.Component { return ( diff --git a/components/api-docs/v2/get-user.js b/components/api-docs/v2/get-user.js index 3d0771a9..418bd29a 100644 --- a/components/api-docs/v2/get-user.js +++ b/components/api-docs/v2/get-user.js @@ -60,7 +60,7 @@ export default class APIDocsGetUser extends React.Component { return ( diff --git a/components/api-docs/v2/get.js b/components/api-docs/v2/get.js index ee1daaaf..7a9a6555 100644 --- a/components/api-docs/v2/get.js +++ b/components/api-docs/v2/get.js @@ -119,7 +119,7 @@ export default class APIDocsGet extends React.Component { return ( diff --git a/components/api-docs/v2/update-file.js b/components/api-docs/v2/update-file.js index e4105243..3f66d2b9 100644 --- a/components/api-docs/v2/update-file.js +++ b/components/api-docs/v2/update-file.js @@ -61,7 +61,7 @@ export default class APIDocsUpdateFile extends React.Component { return ( diff --git a/components/api-docs/v2/update-slate.js b/components/api-docs/v2/update-slate.js index a1be6f9a..74f68f39 100644 --- a/components/api-docs/v2/update-slate.js +++ b/components/api-docs/v2/update-slate.js @@ -60,7 +60,7 @@ export default class APIDocsUpdateCollection extends React.Component { return ( diff --git a/components/api-docs/v2/upload.js b/components/api-docs/v2/upload.js index 50f57d53..8dab60d1 100644 --- a/components/api-docs/v2/upload.js +++ b/components/api-docs/v2/upload.js @@ -75,8 +75,8 @@ export default class APIDocsUploadToSlate extends React.Component { return ( @@ -94,7 +94,7 @@ export default class APIDocsUploadToSlate extends React.Component { children={slateUploadCode} style={{ maxWidth: "820px" }} language={language} - title="Upload to collection" + title="Upload file to collection" multiLang="true" onLanguageChange={this.props.onLanguageChange} /> diff --git a/node_common/request-utilities.js b/node_common/request-utilities.js new file mode 100644 index 00000000..856a9b40 --- /dev/null +++ b/node_common/request-utilities.js @@ -0,0 +1,79 @@ +import * as Data from "~/node_common/data"; +import * as Strings from "~/common/strings"; +import * as Utilities from "~/node_common/utilities"; + +export const checkAuthorizationInternal = async (req, res) => { + const id = Utilities.getIdFromCookie(req); + if (!id) { + return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true }); + } + + const user = await Data.getUserById({ + id, + }); + + if (!user) { + return res.status(404).send({ + decorator: "SERVER_USER_NOT_FOUND", + error: true, + }); + } + + if (user.error) { + return res.status(500).send({ + decorator: "SERVER_USER_NOT_FOUND", + error: true, + }); + } + + return { id, user }; +}; + +export const checkAuthorizationExternal = async (req, res) => { + if (Strings.isEmpty(req.headers.authorization)) { + return res.status(404).send({ + decorator: "NO_API_KEY_PROVIDED", + error: true, + }); + } + + const parsed = Strings.getKey(req.headers.authorization); + + const key = await Data.getAPIKeyByKey({ + key: parsed, + }); + + if (!key) { + return res.status(403).send({ + decorator: "NO_MATCHING_API_KEY_FOUND", + error: true, + }); + } + + if (key.error) { + return res.status(500).send({ + decorator: "ERROR_WHILE_VERIFYING_API_KEY", + error: true, + }); + } + + const user = await Data.getUserById({ + id: key.ownerId, + }); + + if (!user) { + return res.status(404).send({ + decorator: "API_KEY_OWNER_NOT_FOUND", + error: true, + }); + } + + if (user.error) { + return res.status(500).send({ + decorator: "ERROR_WHILE_LOCATING_API_KEY_OWNER", + error: true, + }); + } + + return { id, user }; +}; diff --git a/pages/api/v2/create-collection.js b/pages/api/v2/create-collection.js new file mode 100644 index 00000000..e0af643a --- /dev/null +++ b/pages/api/v2/create-collection.js @@ -0,0 +1,94 @@ +import * as Utilities from "~/node_common/utilities"; +import * as Data from "~/node_common/data"; +import * as Strings from "~/common/strings"; +import * as ViewerManager from "~/node_common/managers/viewer"; +import * as SearchManager from "~/node_common/managers/search"; +import * as Monitor from "~/node_common/monitor"; + +export default async (req, res) => { + if (Strings.isEmpty(req.headers.authorization)) { + return res.status(404).send({ + decorator: "NO_API_KEY_PROVIDED", + error: true, + }); + } + + const parsed = Strings.getKey(req.headers.authorization); + + const key = await Data.getAPIKeyByKey({ + key: parsed, + }); + + if (!key) { + return res.status(403).send({ + decorator: "NO_MATCHING_API_KEY_FOUND", + error: true, + }); + } + + if (key.error) { + return res.status(500).send({ + decorator: "ERROR_WHILE_VERIFYING_API_KEY", + error: true, + }); + } + + const user = await Data.getUserById({ + id: key.ownerId, + }); + + if (!user) { + return res.status(404).send({ + decorator: "API_KEY_OWNER_NOT_FOUND", + error: true, + }); + } + + if (user.error) { + return res.status(500).send({ + decorator: "ERROR_WHILE_LOCATING_API_KEY_OWNER", + error: true, + }); + } + + if (!req.body?.data?.name) { + return res.status(500).send({ + decorator: "MUST_PROVIDE_DATA", + error: true, + }); + } + + const slatename = Strings.createSlug(req.body.data.name); + + const existingSlate = await Data.getSlateByName({ + slatename, + ownerId: user.id, + }); + + if (existingSlate) { + return res.status(500).send({ decorator: "EXISTING_SLATE_NAME", error: true }); + } + + const slate = await Data.createSlate({ + ownerId: id, + slatename: Strings.createSlug(req.body.data.name), + isPublic: req.body.data.isPublic, + data: { + name: req.body.data.name, + body: req.body.data.body, + tags: req.body.data.tags, + }, + }); + + if (!slate || slate.error) { + return res.status(500).send({ decorator: "CREATE_COLLECTION_FAILED", error: true }); + } + + ViewerManager.hydratePartial(id, { slates: true }); + + SearchManager.updateSlate(slate, "ADD"); + + Monitor.createSlate({ user, slate }); + + return res.status(200).send({ decorator: "CREATE_COLLECTION", slate }); +}; diff --git a/pages/api/v2/create-link.js b/pages/api/v2/create-link.js index e937736b..fa75bc4b 100644 --- a/pages/api/v2/create-link.js +++ b/pages/api/v2/create-link.js @@ -61,17 +61,17 @@ export default async (req, res) => { if (!slate || slate.error) { slate = null; - decorator = "SERVER_CREATE_LINK_SLATE_NOT_FOUND"; + decorator = "SLATE_NOT_FOUND"; } } let urls; - if (req.body.data.url) { + if (req.body?.data?.url) { urls = [req.body.data.url]; - } else if (req.body.data.urls) { + } else if (req.body?.data?.urls) { urls = req.body.data.urls; } else { - return res.status(400).send({ decorator: "SERVER_CREATE_LINK_NO_LINK_PROVIDED", error: true }); + return res.status(400).send({ decorator: "NO_LINK_PROVIDED", error: true }); } let files = []; @@ -86,7 +86,7 @@ export default async (req, res) => { }); if (!filteredFiles?.length) { - return res.status(400).send({ decorator: "SERVER_CREATE_LINK_DUPLICATE", error: true }); + return res.status(200).send({ decorator: "LINK_DUPLICATE", data: duplicateFiles }); } files = []; @@ -197,6 +197,6 @@ export default async (req, res) => { return res.status(200).send({ decorator, - data: { added, skipped: files.length - added }, + data: filesToAddToSlate, }); }; diff --git a/pages/api/v2/get-collection.js b/pages/api/v2/get-collection.js index 7fc56176..df41e359 100644 --- a/pages/api/v2/get-collection.js +++ b/pages/api/v2/get-collection.js @@ -43,7 +43,7 @@ export default async (req, res) => { return res.status(500).send({ decorator: "ERROR_WHILE_LOCATING_API_KEY_OWNER", error: true }); } - let slateId = req.body.data ? req.body.data.id : null; + let slateId = req.body?.data?.id; let slate; if (Strings.isEmpty(slateId)) { @@ -73,5 +73,5 @@ export default async (req, res) => { }); } - return res.status(200).send({ decorator: "V2_GET_COLLECTION", collection: slate }); + return res.status(200).send({ decorator: "GET_COLLECTION", collection: slate }); }; diff --git a/pages/api/v2/get-user.js b/pages/api/v2/get-user.js index ab84b2ba..9b83efd2 100644 --- a/pages/api/v2/get-user.js +++ b/pages/api/v2/get-user.js @@ -43,7 +43,7 @@ export default async (req, res) => { return res.status(500).send({ decorator: "ERROR_WHILE_LOCATING_API_KEY_OWNER", error: true }); } - let userId = req.body.data ? req.body.data.id : null; + let userId = req.body?.data?.id; if (Strings.isEmpty(userId)) { return res.status(400).send({ decorator: "NO_USER_ID_PROVIDED", error: true }); @@ -70,5 +70,5 @@ export default async (req, res) => { }); } - return res.status(200).send({ decorator: "V2_GET_USER", user: targetUser }); + return res.status(200).send({ decorator: "GET_USER", user: targetUser }); }; diff --git a/pages/api/v2/get.js b/pages/api/v2/get.js index e6193ca1..5f23963f 100644 --- a/pages/api/v2/get.js +++ b/pages/api/v2/get.js @@ -72,5 +72,5 @@ export default async (req, res) => { return each; }); - return res.status(200).send({ decorator: "V2_GET", user, collections: slates }); + return res.status(200).send({ decorator: "GET", user, collections: slates }); }; diff --git a/pages/api/v2/update-collection.js b/pages/api/v2/update-collection.js index f3725614..2fcd8dbb 100644 --- a/pages/api/v2/update-collection.js +++ b/pages/api/v2/update-collection.js @@ -50,9 +50,9 @@ export default async (req, res) => { }); } - if (!req.body.data) { + if (!req.body?.data?.id) { return res.status(500).send({ - decorator: "V2_UPDATE_COLLECTION_MUST_PROVIDE_DATA", + decorator: "UPDATE_COLLECTION_MUST_PROVIDE_DATA", error: true, }); } @@ -166,5 +166,5 @@ export default async (req, res) => { ViewerManager.hydratePartial(user.id, { slates: true }); - return res.status(200).send({ decorator: "V2_UPDATE_COLLECTION", collection: updatedSlate }); + return res.status(200).send({ decorator: "UPDATE_COLLECTION", collection: updatedSlate }); }; diff --git a/pages/api/v2/update-file.js b/pages/api/v2/update-file.js index d920a748..f5dec7e9 100644 --- a/pages/api/v2/update-file.js +++ b/pages/api/v2/update-file.js @@ -49,7 +49,7 @@ export default async (req, res) => { }); } - if (!req.body.data?.id) { + if (!req.body?.data?.id) { return res.status(500).send({ decorator: "NO_FILE_ID_PROVIDED", error: true }); } @@ -105,7 +105,7 @@ export default async (req, res) => { ViewerManager.hydratePartial(user.id, { library: true, slates: true }); return res.status(200).send({ - decorator: "V2_UPDATE_FILE", + decorator: "UPDATE_FILE", file: response, }); }; diff --git a/scenes/SceneSettingsDeveloper.js b/scenes/SceneSettingsDeveloper.js index dab67b2a..ddd434cf 100644 --- a/scenes/SceneSettingsDeveloper.js +++ b/scenes/SceneSettingsDeveloper.js @@ -26,6 +26,7 @@ import APIDocsUpdateSlateV2 from "~/components/api-docs/v2/update-slate.js"; import APIDocsUpdateFileV2 from "~/components/api-docs/v2/update-file.js"; import APIDocsUploadToSlateV2 from "~/components/api-docs/v2/upload.js"; import APIDocsCreateLinkV2 from "~/components/api-docs/v2/create-link.js"; +import APIDocsCreateCollectionV2 from "~/components/api-docs/v2/create-collection.js"; import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper"; const STYLES_API_KEY = css` @@ -312,7 +313,9 @@ export default class SceneSettingsDeveloper extends React.Component { {tab === "v2" ? ( <> + Read operations + + Update operations + + Create operations + ) : ( <> + Read operations + Update operations + Create operations