From b09d19a6ad53a4e1060786b95119b28db19e4c27 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 24 Jan 2022 13:34:38 +0100 Subject: [PATCH] Implemented api specification --- js/api.js | 160 ++++++++++++++++++++++++++++++++++++ js/api.js.map | 1 + js/api.ts | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 js/api.js create mode 100644 js/api.js.map create mode 100644 js/api.ts diff --git a/js/api.js b/js/api.js new file mode 100644 index 0000000..bce36ff --- /dev/null +++ b/js/api.js @@ -0,0 +1,160 @@ +const prefix = ""; +async function unknownResponse(resp) { + let json = await resp.json(); + try { + return new Error(`${json["message"]} (${resp.status})`); + } + catch (error) { + return new Error(`Server sent unknown error: ${await resp.text()} (${resp.status})`); + } +} +async function login(username, password) { + let result = await fetch(`${prefix}/api/login`, { + method: "POST", + body: JSON.stringify({ "username": username, "password": password }) + }); + switch (result.status) { + case 200: + return; + case 401: + throw new Error("Wrong username and/or password"); + default: + throw await unknownResponse(result); + } +} +async function authors(name, limit) { + let result = await fetch(`${prefix}/api/authors?name=${name}&limit=${limit}`); + if (result.status == 200) { + return await result.json(); + } + else { + throw await unknownResponse(result); + } +} +async function tags(name, limit) { + let result = await fetch(`${prefix}/api/tags?name=${name}&limit=${limit}`); + if (result.status == 200) { + return await result.json(); + } + else { + throw await unknownResponse(result); + } +} +async function recent(limit = 20) { + let result = await fetch(`${prefix}/api/recent?limit=${limit}`); + if (result.status == 200) { + return await result.json(); + } + else { + throw await unknownResponse(result); + } +} +async function search(query) { + let result = await fetch(`${prefix}/api/search?q=${query.query}&from=${query.from.getSeconds()}&to=${query.to.getSeconds()}&authors=${JSON.stringify(query.authors)}&tags=${JSON.stringify(query.tags)}&limit=${query.limit}`); + if (result.status == 200) { + return await result.json(); + } + else { + throw unknownResponse(result); + } +} +async function uploadArticle(payload) { + let result = await fetch(`${prefix}/api/article`, { + method: "POST", + body: JSON.stringify(payload) + }); + switch (result.status) { + case 201: + return await result.json(); + case 401: + throw new Error("Not authorized"); + case 409: + throw new Error("An article with the same title already exists"); + default: + throw await unknownResponse(result); + } +} +async function editArticle(payload) { + let result = await fetch(`${prefix}/api/article`, { + method: "PATCH", + body: JSON.stringify(payload) + }); + let json = await result.json(); + switch (result.status) { + case 201: + return json; + case 401: + throw new Error("Not authorized"); + case 404: + throw new Error("Could not find article"); + case 409: + throw new Error("An article with the same title already exists"); + default: + throw await unknownResponse(result); + } +} +async function deleteArticle(id) { + let result = await fetch(`${prefix}/api/article`, { + method: "DELETE", + body: JSON.stringify({ "id": id }) + }); + switch (result.status) { + case 200: + return; + case 401: + throw new Error("Not authorized"); + case 404: + throw new Error("Could not find article"); + default: + throw await unknownResponse(result); + } +} +async function getAssets(name, limit = 20) { + let result = await fetch(`${prefix}/api/assets?q=${name}&limit=${limit}`, { + method: "GET", + }); + switch (result.status) { + case 200: + return await result.json(); + case 401: + throw new Error("Not authorized"); + default: + throw await unknownResponse(result); + } +} +async function addAsset(name, content) { + let result = await fetch(`${prefix}/api/assets`, { + method: "POST", + body: JSON.stringify({ + "name": name, + "content": content, + }) + }); + switch (result.status) { + case 201: + return await result.json(); + case 401: + throw new Error("Not authorized"); + case 409: + throw new Error("An asset with the same name already exists"); + default: + throw await unknownResponse(result); + } +} +async function deleteAsset(id) { + let result = await fetch(`${prefix}/api/assets`, { + method: "DELETE", + body: JSON.stringify({ "id": id }) + }); + switch (result.status) { + case 200: + return; + case 401: + throw new Error("Not authorized"); + case 404: + throw new Error("An asset with this id does not exist"); + default: + throw await unknownResponse(result); + } +} +//# sourceMappingURL=api.js.map \ No newline at end of file diff --git a/js/api.js.map b/js/api.js.map new file mode 100644 index 0000000..b4cc400 --- /dev/null +++ b/js/api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"api.js","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/js/api.ts b/js/api.ts new file mode 100644 index 0000000..bbe1821 --- /dev/null +++ b/js/api.ts @@ -0,0 +1,222 @@ +const prefix = "" + + +async function unknownResponse(resp: Response): Promise { + let json = await resp.json() + try { + return new Error(`${json["message"]} (${resp.status})`) + } catch (error) { + return new Error(`Server sent unknown error: ${await resp.text()} (${resp.status})`) + } +} + +interface Author { + id: number, + name: string, + information: string +} + +interface ArticleSummary { + id: number, + title: string, + summary: string + authors: Author[], + image?: string, + tags: string[], + created: Date, + modified?: Date, + link: string +} + +interface Asset { + id: number, + name: string, + link: string +} + +async function login(username: string, password: string): Promise { + let result = await fetch(`${prefix}/api/login`, { + method: "POST", + body: JSON.stringify({"username": username, "password": password}) + }) + switch (result.status) { + case 200: + return + case 401: + throw new Error("Wrong username and/or password") + default: + throw await unknownResponse(result) + } +} + +async function authors(name?: string, limit?: number): Promise { + let result = await fetch(`${prefix}/api/authors?name=${name}&limit=${limit}`) + if (result.status == 200) { + return await result.json() + } else { + throw await unknownResponse(result) + } +} + +async function tags(name?: string, limit?: number): Promise { + let result = await fetch(`${prefix}/api/tags?name=${name}&limit=${limit}`) + if (result.status == 200) { + return await result.json() + } else { + throw await unknownResponse(result) + } +} + +async function recent(limit: number = 20): Promise { + let result = await fetch(`${prefix}/api/recent?limit=${limit}`) + if (result.status == 200) { + return await result.json() + } else { + throw await unknownResponse(result) + } +} + +interface SearchQuery { + query?: string, + from?: Date, + to?: Date, + authors?: number[], + tags?: string[], + limit?: number +} + +async function search(query: SearchQuery): Promise { + let result = await fetch(`${prefix}/api/search?q=${query.query}&from=${query.from.getSeconds()}&to=${query.to.getSeconds()}&authors=${JSON.stringify(query.authors)}&tags=${JSON.stringify(query.tags)}&limit=${query.limit}`) + if (result.status == 200) { + return await result.json() + } else { + throw unknownResponse(result) + } +} + +interface ArticleUploadPayload { + title: string, + summary: string, + authors: number[], + image?: string, + tags: string[], + link?: string + content: string +} + +async function uploadArticle(payload: ArticleUploadPayload): Promise { + let result = await fetch(`${prefix}/api/article`, { + method: "POST", + body: JSON.stringify(payload) + }) + switch (result.status) { + case 201: + return await result.json() + case 401: + throw new Error("Not authorized") + case 409: + throw new Error("An article with the same title already exists") + default: + throw await unknownResponse(result) + } +} + +interface ArticleEditPayload { + id: number, + title?: string, + summary?: string, + authors?: number[], + image?: string, + tags?: string[], + link?: string + content?: string +} + +async function editArticle(payload: ArticleEditPayload): Promise { + let result = await fetch(`${prefix}/api/article`, { + method: "PATCH", + body: JSON.stringify(payload) + }) + let json = await result.json() + + switch (result.status) { + case 201: + return json + case 401: + throw new Error("Not authorized") + case 404: + throw new Error("Could not find article") + case 409: + throw new Error("An article with the same title already exists") + default: + throw await unknownResponse(result) + } +} + +async function deleteArticle(id: number): Promise { + let result = await fetch(`${prefix}/api/article`, { + method: "DELETE", + body: JSON.stringify({"id": id}) + }) + switch (result.status) { + case 200: + return + case 401: + throw new Error("Not authorized") + case 404: + throw new Error("Could not find article") + default: + throw await unknownResponse(result) + } +} + +async function getAssets(name?: string, limit: number = 20): Promise { + let result = await fetch(`${prefix}/api/assets?q=${name}&limit=${limit}`, { + method: "GET", + }) + switch (result.status) { + case 200: + return await result.json() + case 401: + throw new Error("Not authorized") + default: + throw await unknownResponse(result) + } +} + +async function addAsset(name: string, content: string): Promise { + let result = await fetch(`${prefix}/api/assets`, { + method: "POST", + body: JSON.stringify({ + "name": name, + "content": content, + }) + }) + switch (result.status) { + case 201: + return await result.json() + case 401: + throw new Error("Not authorized") + case 409: + throw new Error("An asset with the same name already exists") + default: + throw await unknownResponse(result) + } +} + +async function deleteAsset(id: number): Promise { + let result = await fetch(`${prefix}/api/assets`, { + method: "DELETE", + body: JSON.stringify({"id": id}) + }) + switch (result.status) { + case 200: + return + case 401: + throw new Error("Not authorized") + case 404: + throw new Error("An asset with this id does not exist") + default: + throw await unknownResponse(result) + } +}