diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2475cf6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": [ + ["@babel/preset-env",{"targets": {"node": "current"}}], + "@babel/preset-typescript" + ], + "plugins": [ + "@babel/plugin-syntax-bigint", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-optional-chaining" + ] +} diff --git a/package.json b/package.json index 02d23e6..e6c02dd 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,15 @@ { "name": "snek-client", - "version": "0.1.1", + "version": "0.2.0", "description": "Enoy it.", "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "npx tsc" + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch", + "build": "npm run build:types && npm run build:js", + "build:types": "tsc --emitDeclarationOnly", + "build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline" }, "files": [ "lib", @@ -24,17 +27,23 @@ }, "homepage": "https://github.com/snek-at/client#readme", "dependencies": { - "apollo-cache-inmemory": "^1.6.5", - "apollo-client": "^2.6.8", - "apollo-link-http": "^1.5.16", - "graphql": "^14.6.0", + "@apollo/client": "^3.1.5", + "@types/node": "^14.14.10", + "apollo-upload-client": "^14.1.1", + "graphql": "^14.7.0", "graphql-tag": "^2.10.3", "js-cookie": "^2.2.1" }, "devDependencies": { - "@babel/core": "^7.9.6", - "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/cli": "^7.12.8", + "@babel/core": "^7.12.9", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/preset-env": "^7.12.7", + "@babel/preset-typescript": "^7.12.7", + "@types/apollo-upload-client": "^14.1.0", "@types/js-cookie": "^2.2.6", - "typescript": "^3.8.3" + "typescript": "^3.9.7" } } diff --git a/src/config.json b/src/config.json new file mode 100644 index 0000000..ab8d19a --- /dev/null +++ b/src/config.json @@ -0,0 +1,6 @@ +{ + "anonUser": { + "username": "cisco", + "password": "ciscocisco" + } +} \ No newline at end of file diff --git a/src/endpoints/apollo.ts b/src/endpoints/apollo.ts index b263f56..b0e8478 100644 --- a/src/endpoints/apollo.ts +++ b/src/endpoints/apollo.ts @@ -1,20 +1,15 @@ //#region > Imports -//#PACKAGE "apollo-client" -//## npm install "apollo-client"@2.6.8 -// Contains the client for graphql handling -import { ApolloClient } from "apollo-client"; -//#PACKAGE "apollo-link-http" -//## npm install "apollo-link-http"@1.5.16 -// Contains the link for the apollo client -import { HttpLink } from "apollo-link-http"; -//#PACKAGE "apollo-cache-inmemory" -//## npm install "apollo-cache-inmemory"@1.6.5 -// Contains cache handling for apollo +//#PACKAGE "@apollo/client" import { + ApolloClient, + ApolloLink, InMemoryCache, - IntrospectionFragmentMatcher, NormalizedCacheObject, -} from "apollo-cache-inmemory"; + HttpLink, +} from "@apollo/client"; +//#PACKAGE "'apollo-upload-client" +// Contains the link for the apollo client +import { createUploadLink } from "apollo-upload-client"; //#PACKAGE "graphql" //## npm install "graphql"@14.6.0 // Contains the interface for gql queries, mutations and subscriptions @@ -32,7 +27,7 @@ import { ApolloResult } from "./index"; /** @class Apollo client for graphql handling */ class Apollo implements ApolloEndpoint { //> Fields - private link: HttpLink; + private link: ApolloLink; private cache: InMemoryCache; private client: ApolloClient; @@ -49,26 +44,18 @@ class Apollo implements ApolloEndpoint { */ constructor(uri: string, options: Options) { this.headers = options.headers; - const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData: { - __schema: { - types: [], - }, - }, - }); try { - this.cache = new InMemoryCache({ fragmentMatcher }); + this.cache = new InMemoryCache(); } catch { //#ERROR throw new Error("An error occurred while initializing the cache!"); } try { - this.link = new HttpLink({ - uri, - headers: options.headers, - }); + const uploadLink = createUploadLink({ uri, headers: options.headers }); + + this.link = ApolloLink.from([uploadLink]); } catch { //#ERROR throw new Error("An error occurred while initializing the API link!"); diff --git a/src/endpoints/index.ts b/src/endpoints/index.ts index 69a99a9..1f720df 100644 --- a/src/endpoints/index.ts +++ b/src/endpoints/index.ts @@ -4,8 +4,7 @@ //## npm install "graphql"@14.6.0 // Contains the interface for gql queries, mutations and subscriptions import { DocumentNode } from "graphql"; -import { FetchResult } from "apollo-link"; -import { ApolloQueryResult } from "apollo-client"; +import { ApolloQueryResult, FetchResult } from "@apollo/client"; //#endregion //#region > Types @@ -75,21 +74,21 @@ interface ScraperEndpoint extends Endpoint { /** * GetJson: A method which gets json data from a specific url. * - * @param url A web url + * @param path Path to the endpoint * @returns {Promise} Json data in the given format */ getJson(url: string): Promise; /** * GetDom: A method which gets DOM data from a specific url. * - * @param url A web url + * @param path Path to the endpoint * @returns {Promise} A DOM Document */ getDom(url: string): Promise; /** * Post: A method to post data to a specific url. * - * @param {string} url A web url + * @param {string} path Path to the endpoint * @param data Data which is filled into the body of a post request * @returns {Promise} A DOM Document */ @@ -106,6 +105,52 @@ interface ScraperEndpoint extends Endpoint { | null | undefined ): Promise; + /** + * Send fetch request to a endpoint and get the respective result. + * + * @param {string} path Path to the endpoint. Specify it like "/foo/bar". + * The correct placement of the slashes is essential! + * @param {"GET" | "POST" | "PUT" | "PATCH" | "DELETE"} type HTTP methods + * @param data Data which is filled into the body of a post request + * @returns {Promise} A DOM Document + */ + fetch( + path: string, + type: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", + data?: + | string + | Blob + | ArrayBufferView + | ArrayBuffer + | FormData + | URLSearchParams + | ReadableStream + | null + | undefined + ): Promise; + /** + * Send fetch request to a endpoint and get the respective JSON result. + * + * @param {string} path Path to the endpoint. Specify it like "/foo/bar". + * The correct placement of the slashes is essential! + * @param {"GET" | "POST" | "PUT" | "PATCH" | "DELETE"} type HTTP methods + * @param data Data which is filled into the body of a post request + * @returns {Promise} A DOM Document + */ + fetchJson( + path: string, + type: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", + data?: + | string + | Blob + | ArrayBufferView + | ArrayBuffer + | FormData + | URLSearchParams + | ReadableStream + | null + | undefined + ): Promise; } //#endregion diff --git a/src/endpoints/scraper.ts b/src/endpoints/scraper.ts index c239b70..89479c6 100644 --- a/src/endpoints/scraper.ts +++ b/src/endpoints/scraper.ts @@ -69,22 +69,9 @@ class Scraper implements ScraperEndpoint { * @returns {object} DOM object */ async getDom(path: string): Promise { - return fetch(this.url + path, { - headers: { - ...this.headers, - }, - }) - .then((response) => { - if (!response.ok) { - //#ERROR - throw new Error(response.statusText); - } - - return response.text(); - }) - .then((text) => { - return new DOMParser().parseFromString(text, "text/html"); - }); + const text = await (await this.fetch(path, "GET")).text(); + + return new DOMParser().parseFromString(text, "text/html"); } /** @@ -123,6 +110,78 @@ class Scraper implements ScraperEndpoint { return response.json().then((data) => data as T); }); } + + /** + * Send fetch request to a endpoint and get the respective result. + * + * @param {string} path Path to the endpoint. Specify it like "/foo/bar". + * The correct placement of the slashes is essential! + * @param {"GET" | "POST" | "PUT" | "PATCH" | "DELETE"} type HTTP methods + * @param data Data which is filled into the body of a post request + * @returns {Promise} A DOM Document + */ + async fetch( + path: string, + type: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", + data?: + | string + | Blob + | ArrayBufferView + | ArrayBuffer + | FormData + | URLSearchParams + | ReadableStream + | null + | undefined + ): Promise { + return fetch(this.url + path, { + method: type, + body: data, + headers: { + ...this.headers, + }, + }).then(async (response) => { + if (!response.ok) { + //#ERROR + throw new Error(response.statusText); + } + + return response; + }); + } + + /** + * Send fetch request to a endpoint and get the respective JSON result. + * + * @param {string} path Path to the endpoint. Specify it like "/foo/bar". + * The correct placement of the slashes is essential! + * @param {"GET" | "POST" | "PUT" | "PATCH" | "DELETE"} type HTTP methods + * @param data Data which is filled into the body of a post request + * @returns {Promise} A DOM Document + */ + async fetchJson( + path: string, + type: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", + data?: + | string + | Blob + | ArrayBufferView + | ArrayBuffer + | FormData + | URLSearchParams + | ReadableStream + | null + | undefined + ): Promise { + return this.fetch(path, type, data).then(async (response) => { + if (!response.ok) { + //#ERROR + throw new Error(response.statusText); + } + + return response.json().then((data) => data as T); + }); + } } //#endregion diff --git a/src/index.ts b/src/index.ts index 81d35e9..97cbc5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,17 +4,16 @@ import Apollo from "./endpoints/apollo"; // Contains the scraper endpoint import Scraper from "./endpoints/scraper"; -//> Templates -// Contains the main template -import { MainTemplate } from "./templates/index"; //> Sessions // Contains the SNEK and github session -import { SnekSession, GithubSession } from "./session/sessions"; +import { + SnekSession, + GithubSession, + InstagramSession, +} from "./session/sessions"; //> Interfaces // Contains interfaces for scraper and apollo import { ScraperEndpoint, ApolloEndpoint } from "./endpoints/index"; -// Contains the interface for the main template -import { IMainTemplate } from "./templates/index"; //#endregion //#region > Interfaces @@ -64,7 +63,6 @@ class Client implements IClient { /** @class A client implementation for SNEK interaction */ class SnekClient extends Client { gql: ApolloEndpoint; - template: IMainTemplate; session: SnekSession; /** @@ -78,22 +76,20 @@ class SnekClient extends Client { * @param type A type description to differ between multiple instances */ constructor( - url: string = "https://engine.snek.at/api/graphiql", + url: string = "https://engine.snek.at/graphql", headers: object = {}, type: string = "graphql" ) { super({ type, url, headers }); - this.template = new MainTemplate(); this.gql = new Apollo(url, { headers }); - this.session = new SnekSession("snek", this.gql, this.template.snek); + this.session = new SnekSession("snek", this.gql); } } /** @class A client implementation for github interaction */ class GithubClient extends Client { gql: ApolloEndpoint; - template: IMainTemplate; session: GithubSession; /** @@ -113,9 +109,39 @@ class GithubClient extends Client { ) { super({ type, url, headers }); - this.template = new MainTemplate(); this.gql = new Apollo(url, { headers }); - this.session = new GithubSession("github", this.gql, this.template); + this.session = new GithubSession("github", this.gql); + } +} + +/** @class A client implementation for instagram interaction */ +class InstagramClient extends Client { + ep: ScraperEndpoint; + session: InstagramSession; + + /** + * Initializes a Github client. + * + * @constructor + * @author Nico Schett + * @param url The base URL the InstagramClient should be working on. + * Default: "https://graph.instagram.com" + * @param headers A object containing various request headers + * @param type A type description to differ between multiple instances + */ + constructor( + accessToken: string, + url: string = "https://graph.instagram.com", + headers: {} = {}, + type: string = "scraper" + ) { + super({ type, url, headers }); + + this.ep = new Scraper(url, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + this.session = new InstagramSession("instagram", accessToken, this.ep); } } @@ -148,7 +174,7 @@ class WebClient extends Client { //#endregion //#region > Exports -export { SnekClient, GithubClient, WebClient }; +export { SnekClient, GithubClient, InstagramClient, WebClient }; //#endregion /** diff --git a/src/session/index.ts b/src/session/index.ts index 8808a4a..c9049bd 100644 --- a/src/session/index.ts +++ b/src/session/index.ts @@ -24,6 +24,7 @@ interface ISession { class Session implements ISession { sessions: { [id: string]: ISession } = {}; tokenName: string = "token"; + _token: string | undefined = undefined; /** * Initializes a base Session. @@ -41,9 +42,11 @@ class Session implements ISession { * @returns {string | undefined} A users JWT if set */ get token(): string | undefined { - const token = Cookies.get(this.tokenName); - - return token ? token : undefined; + if (this.checkPlatform() === "WEB") { + return Cookies.get(this.tokenName); + } else { + return this._token; + } } //> Setter @@ -55,14 +58,31 @@ class Session implements ISession { * the cookie will be removed. */ set token(value: string | undefined) { - if (value) { - Cookies.set(this.tokenName, value); + if (this.checkPlatform() === "WEB") { + if (value) { + Cookies.set(this.tokenName, value ? value : "", { + /* Expire time is set to 4 minutes */ + expires: 4 / 1440, + }); + } else { + Cookies.remove(this.tokenName); + } } else { - Cookies.remove(this.tokenName); + this._token = value; } } //> Methods + /** + * Check if snek-client is used by node or web. + */ + checkPlatform() { + if (typeof window === "undefined") { + return "NODE"; + } else { + return "WEB"; + } + } /** * Add a subSession to a session. * diff --git a/src/session/sessions.ts b/src/session/sessions.ts index 33cdc18..3edfa84 100644 --- a/src/session/sessions.ts +++ b/src/session/sessions.ts @@ -11,19 +11,25 @@ import Cookies from "js-cookie"; //> Session // Contains the base session import Session from "./index"; -//> Templates -// Contains the main template for the sessions -import { IMainTemplate } from "../templates/index"; -// Contains the SNEK template -import SnekTemplate from "../templates/snek/index"; //> Tasks // Contains SNEK tasks -import SnekTasks from "../templates/snek/gql/tasks/index"; +import SnekTasks from "../templates/snek/tasks/index"; +import InstagramTasks from "../templates/instagram/tasks"; //> Interfaces -// Contains the interface for the apollo endpoint -import { ApolloEndpoint } from "../endpoints/index"; +// Contains the interface for the apollo and scraper endpoint +import { + ApolloEndpoint, + ScraperEndpoint, + ApolloResult, +} from "../endpoints/index"; // Contains basic session interfaces import { User } from "./index"; +// Contains the session types +import { SNEKAuth } from "./types"; +// Contains the snek template types +import { TaskTypes } from "../templates/snek/types"; +//> Config +import Config from "../config.json"; //#endregion //#region > Classes @@ -36,14 +42,8 @@ class GithubSession extends Session { * @author Nico Schett * @param {string} sId A session name * @param {Endpoint} ep A endpoint - * @param {IMainTemplate} template A template set */ - constructor( - sId: string, - public ep: ApolloEndpoint, - public template: IMainTemplate - ) { - /** @todo Change template set to dedicated github template set */ + constructor(sId: string, public ep: ApolloEndpoint) { super(sId); } @@ -66,6 +66,7 @@ class GithubSession extends Session { /** @class CookieSession extends token session handling with cookies */ class CookieSession extends Session { refreshTokenName: string = "refresh"; + _refreshToken: string | undefined = undefined; /** * Initializes a cookie session. @@ -94,9 +95,11 @@ class CookieSession extends Session { * @returns {string | undefined} A users JWT if set */ get refreshToken(): string | undefined { - const token = Cookies.get(this.refreshTokenName); - - return token ? token : undefined; + if (this.checkPlatform() === "WEB") { + return Cookies.get(this.refreshTokenName); + } else { + return this._token; + } } //> Setter @@ -109,14 +112,7 @@ class CookieSession extends Session { * minutes. */ set token(value: string | undefined) { - if (value) { - Cookies.set(this.tokenName, value ? value : "", { - /* Expire time is set to 4 minutes */ - expires: 4 / 1440, - }); - } else { - Cookies.remove(this.tokenName); - } + super.token = value; } /** @@ -128,14 +124,76 @@ class CookieSession extends Session { * set to six days. */ set refreshToken(value: string | undefined) { - if (value) { - Cookies.set(this.refreshTokenName, value, { - /* Expire time is set to 6 days */ - expires: 6, - }); + if (this.checkPlatform() === "WEB") { + if (value) { + Cookies.set(this.refreshTokenName, value ? value : "", { + /* Expire time is set to 6 days */ + expires: 6, + }); + } else { + Cookies.remove(this.refreshTokenName); + } } else { - Cookies.remove(this.refreshTokenName); + this._refreshToken = value; + } + } +} + +class InstagramSession extends Session { + /* Define tasks */ + public tasks: InstagramTasks; + + /** + * Initializes a Instagram session. + * + * @constructor + * @author Nico Schett + * @param {string} sId A session name + * @param {string} token A instagram access token + * @param {Endpoint} ep A endpoint + */ + constructor(sId: string, token: string, public ep: ScraperEndpoint) { + super(sId); + + this.tokenName = sId + "-" + this.tokenName; + this.token = token; + this.tasks = new InstagramTasks(this); + } + + //> Methods + /** + * Refreshes the current set access token. + * + * @returns {Promise} The session token if set + */ + async upToDateToken(): Promise { + let token = super.token; + + /* Refresh token if there is none */ + if (!token) { + await this.refresh(); + + token = super.token; } + + return token; + } + + /** + * Refreshes a session. + */ + async refresh() { + this.token = await this.tasks.refreshToken(); + } + + /** + * The runner allows to access every endpoint of the instagram api without + * caring about refreshing the access tokens. + */ + async getRunner() { + this.ep.headers = { Authorization: `Bearer ${await this.upToDateToken()}` }; + + return this.ep; } } @@ -151,13 +209,8 @@ class SnekSession extends CookieSession { * @author Nico Schett * @param {string} sId A session name * @param {Endpoint} ep A endpoint - * @param {SnekTemplate} template A template set */ - constructor( - sId: string, - public ep: ApolloEndpoint, - public template: SnekTemplate - ) { + constructor(sId: string, public ep: ApolloEndpoint) { super(sId); this.tokenName = sId + "-" + this.tokenName; @@ -203,34 +256,41 @@ class SnekSession extends CookieSession { * Begin session. * * @param {string} user A User defined by username and password - * @returns {Promise} A UserData object + * @returns {Promise} A SNEKAuth object */ - async begin(user?: User): Promise { - let response; + async begin(user?: User): Promise { + let anonymous = false; if (!user && this.refreshToken) { /* Refresh token and retrieve a new refreshToken if necessary */ this.refresh(); } else { + let authData; + if (!user) { /* Authenticate anonymous user */ - response = await this.tasks.auth.anon(); + authData = (await this.tasks.set.auth.anon()).data?.auth; + anonymous = true; } else { /* Authenticate real user */ - response = await this.tasks.auth.nonanon(user); + authData = (await this.tasks.set.auth.nonanon(user)).data?.auth; } /* Set tokens */ - this.token = response.data?.auth.token; - this.refreshToken = response.data?.auth.refreshToken; + this.token = authData?.token; + this.refreshToken = authData?.refreshToken; - return response.data?.auth.user; + return { anonymous, ...authData?.user }; } /* Get user data */ - response = await this.tasks.user.whoami(); + const userData = (await this.tasks.set.user.whoami()).data?.whoami; + + if (userData?.username === Config.anonUser.username) { + anonymous = true; + } - return response.data?.whoami; + return { anonymous, ...userData }; } /** @@ -242,7 +302,7 @@ class SnekSession extends CookieSession { async refresh() { if (!this.token) { if (this.refreshToken) { - let response = await this.tasks.auth.refresh(); + let response = await this.tasks.set.auth.refresh(); this.token = response.data?.refresh.token; this.refreshToken = response.data?.refresh.refreshToken; @@ -261,7 +321,7 @@ class SnekSession extends CookieSession { async end() { /* Revoke token if it is set */ if (this.refreshToken !== "") { - let response = await this.tasks.auth.revoke(); + let response = await this.tasks.set.auth.revoke(); //#DEBUG TSID1 //console.log("TID-1(REVOKE)", response.data?.revoke.revoked); @@ -278,22 +338,39 @@ class SnekSession extends CookieSession { * @param type * @param data * @param variables + * @param {boolean} retry Retry on error * @description Perform a session aware custom task. Token and refreshToken * are set by default! * When no type is specified, query is set as default. */ - async customTask(type: string, data: DocumentNode, variables: object) { - return this.tasks.run(type, data, { + async runner( + type: TaskTypes, + data: DocumentNode, + variables: object, + retry: boolean = true + ): Promise> { + variables = { ...variables, - token: this.token, + token: await this.upToDateToken(), refreshToken: this.refreshToken, + }; + + const response = await this.tasks.run(type, data, { + ...variables, }); + + if (this.tasks.handleErrors(response) === false && retry) { + await this.refresh(); + + return this.runner(type, data, variables, false); + } + return response; } } //#endregion //#region > Exports -export { GithubSession, SnekSession }; +export { GithubSession, InstagramSession, SnekSession }; //#endregion /** diff --git a/src/session/types.ts b/src/session/types.ts new file mode 100644 index 0000000..1b585b0 --- /dev/null +++ b/src/session/types.ts @@ -0,0 +1,4 @@ +export interface SNEKAuth { + username?: string; + anonymous: boolean; +} diff --git a/src/templates/index.ts b/src/templates/index.ts deleted file mode 100644 index c8b6f55..0000000 --- a/src/templates/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -//#region > Imports -//> SNEK Templates -import SnekTemplate from "./snek"; -//#endregion - -//#region > Interfaces -/** @interface IMainTemplate defines the structure of the base templates */ -interface IMainTemplate { - snek: SnekTemplate; -} -//#endregion - -//#region > Classes -/** @class A Template which initializes all SubTemplates */ -class MainTemplate implements IMainTemplate { - public snek = new SnekTemplate(); -} -//#endregion - -//#region > Exports -export type { IMainTemplate }; -export { MainTemplate }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/instagram/paths.ts b/src/templates/instagram/paths.ts new file mode 100644 index 0000000..acb1388 --- /dev/null +++ b/src/templates/instagram/paths.ts @@ -0,0 +1,7 @@ +/** + * Uris for instagram api access + * Ref: https://developers.facebook.com/docs/ + */ + +export const TOKEN_REFRESH_PATH = + "/refresh_access_token?grant_type=ig_refresh_token"; diff --git a/src/templates/instagram/tasks.ts b/src/templates/instagram/tasks.ts new file mode 100644 index 0000000..4395a82 --- /dev/null +++ b/src/templates/instagram/tasks.ts @@ -0,0 +1,33 @@ +//#region > Imports +//> Sessions +// Contains the SNEK session +import { InstagramSession } from "../../session/sessions"; +//> Instagram Endpoint paths +import * as paths from "./paths"; +//#endregion + +//#region > Classes +/** @class A Template with initializes all tasks */ +class InstagramTasks { + /** + * Initializes a InstagramSession. + * + * @constructor + * @author Nico Schett + * @param {string} session A session to initialize all corresponding tasks + */ + constructor(public session: InstagramSession) {} + + async refreshToken() { + const runner = await this.session.getRunner(); + + const res = await runner.getJson<{ access_token: string }>( + paths.TOKEN_REFRESH_PATH + ); + + return res.access_token; + } +} + +export default InstagramTasks; +//#endregion diff --git a/src/templates/snek/gql/errors.ts b/src/templates/snek/errors.ts similarity index 78% rename from src/templates/snek/gql/errors.ts rename to src/templates/snek/errors.ts index 5e8fa5d..ac9de80 100644 --- a/src/templates/snek/gql/errors.ts +++ b/src/templates/snek/errors.ts @@ -1,10 +1,10 @@ //#region > Imports //> Sessions // Contains the SNEK session -import { SnekSession } from "../../../session/sessions"; +import { SnekSession } from "../../session/sessions"; //> Types // Contains the type declarations for Apollo results -import { ApolloResult } from "./index"; +import * as types from "./types"; //#endregion //#region > Classes @@ -23,16 +23,14 @@ class TaskError { * Handle specific errors which could occur on SNEK tasks. * * @param {Response} response A SNEK-client graphql response - * @returns {boolean} "false" if an error occurs. Otherwise "true" + * @returns {boolean} "false" if a token error occurs. + * "null" if other errors occurs. + * "true" otherwise. */ - handleErrors(response: ApolloResult): boolean { + handleErrors(response: types.ApolloResult): boolean | null { const errors = response.errors; if (errors && errors.length > 0) { - //#LEGACY - //#ERROR - console.error("An error occurred" + JSON.stringify(response)); - if (errors[0].message === "Invalid refresh token") { /* Delete token and refresh token if refresh token is invalid */ this.session.token = undefined; @@ -45,7 +43,7 @@ class TaskError { return false; } else { - return false; + return null; } } diff --git a/src/templates/snek/gql/index.ts b/src/templates/snek/gql/index.ts deleted file mode 100644 index d9ee2a0..0000000 --- a/src/templates/snek/gql/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -//#region > Imports -//> Queries -import { SnekGqlQuery } from "./queries/index"; -//> Mutations -import { SnekGqlMutation } from "./mutations/index"; -//> Types -// Contains the type declarations for Apollo results -import { ApolloResult } from "../../../endpoints/index"; -//#endregion - -//#region > Interfaces -/** @interface SnekGqlTemplate defines the structure for the gql template */ -interface SnekGqlTemplate { - queries: SnekGqlQuery; - mutations: SnekGqlMutation; -} -//#endregion - -//#region > Classes -/** @class A Template which initializes all SubTemplates */ -class SnekGql { - public queries = new SnekGqlQuery(); - public mutations = new SnekGqlMutation(); -} -//#endregion - -//#region > Exports -export type { SnekGqlTemplate, ApolloResult }; -export default SnekGql; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/mutations/index.ts b/src/templates/snek/gql/mutations/index.ts deleted file mode 100644 index c03cda1..0000000 --- a/src/templates/snek/gql/mutations/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -//#region > Imports -//> Mutations -// Contains the jwtAuth mutations -import * as _jwtAuth from "./jwtAuth"; -// Contains the user mutations -import * as _user from "./user"; -//#endregion - -//#region > Classes -/** @class A Template which initializes all mutations */ -class SnekGqlMutation { - public jwtAuth = _jwtAuth; - public user = _user; -} -//#endregion - -//#region > Exports -export { SnekGqlMutation }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/mutations/user.ts b/src/templates/snek/gql/mutations/user.ts deleted file mode 100644 index 6d70064..0000000 --- a/src/templates/snek/gql/mutations/user.ts +++ /dev/null @@ -1,61 +0,0 @@ -//#region > Imports -//#PACKAGE "graphql-tag" -//## npm install "graphql-tag"@2.10.3 -// Contains a gql tag for wrapping queries -import gql from "graphql-tag"; -//#endregion - -//#region > Queries -/** - * User registration. - * - * @param {string} token A users JWT - * @param {string} values Registration parameters defined by registration page - * @returns {string} Registration conformation - * @description A mutation to register a user by providing values and a valid - * JWT. This could be a JWT of the anonymous user. - */ -const registration = gql` - mutation registration($token: String!, $values: GenericScalar!) { - registration: registrationRegistrationFormPage( - token: $token - url: "/registration" - values: $values - ) { - result - errors { - name - errors - } - } - } -`; - -/** - * Cache user. - * - * @param {string} token A users JWT - * @param {string} platformData A serialized JSON object of all generated user - * data. - * @returns {string} PlatformData of a user - * @description A mutation to cache user information server side - */ -const cache = gql` - mutation cache($token: String!, $platformData: String!) { - cache: cacheUser(token: $token, platformData: $platformData) { - user { - id - } - } - } -`; -//#endregion - -//#region > Exports -export { registration, cache }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/queries/general.ts b/src/templates/snek/gql/queries/general.ts deleted file mode 100644 index 3533f63..0000000 --- a/src/templates/snek/gql/queries/general.ts +++ /dev/null @@ -1,55 +0,0 @@ -//#region > Imports -//#PACKAGE "graphql-tag" -//## npm install "graphql-tag"@2.10.3 -// Contains a gql tag for wrapping queries -import gql from "graphql-tag"; -//#endregion - -//#region > Queries -/** - * List of GitLab server. - * - * @param {string} JWT A users JWT - * @returns {string} A serialized JSON object with a list of GitLab server - * @description A query to fetch all available GitLab server - */ -const gitlabServer = gql` - query gitLabServers($token: String!) { - page(url: "/registration", token: $token) { - ... on RegistrationRegistrationFormPage { - supportedGitlabs { - ... on RegistrationGitlab_Server { - organisation - domain - field - } - } - } - } - } -`; - -/** - * List of page urls. - * - * @param {string} JWT A users JWT - * @returns {string} A serialized JSON object with a list of all page urls - * @description A query to fetch all pages urls - */ -const allPageUrls = gql` - query pages($token: String!) { - pages(token: $token) { - urlPath - } - } -`; -//#endregion - -//#region > Exports -export { gitlabServer, allPageUrls }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/queries/index.ts b/src/templates/snek/gql/queries/index.ts deleted file mode 100644 index df1bca7..0000000 --- a/src/templates/snek/gql/queries/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -//#region > Imports -//> Queries -// Contains the general queries -import * as _general from "./general"; -// Contains the user queries -import * as _user from "./user"; -//#endregion - -//#region > Classes -/** @class A template which initializes all SubTemplates */ -class SnekGqlQuery { - public general = _general; - public user = _user; -} -//#endregion - -//#region > Exports -export { SnekGqlQuery }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/queries/user.ts b/src/templates/snek/gql/queries/user.ts deleted file mode 100644 index bf9977e..0000000 --- a/src/templates/snek/gql/queries/user.ts +++ /dev/null @@ -1,63 +0,0 @@ -//#region > Imports -//#PACKAGE "graphql-tag" -//## npm install "graphql-tag"@2.10.3 -// Contains a gql tag for wrapping queries -import gql from "graphql-tag"; -//#endregion - -//#region > Queries -/** - * Whoami. - * - * @param {string} token A users JWT - * @returns {string} A username - * @description A query to fetch the username of the according token - */ -const whoami = gql` - query whoami($token: String!) { - whoami: me(token: $token) { - username - firstName - lastName - email - dateJoined - lastLogin - } - } -`; - -/** - * Get user profile. - * - * @param {string} url PageUrl of a user profile - * @param {string} token A users JWT - * @returns {string} A profile page of a user - * @description A query to fetch profile data - */ -const profile = gql` - query profile($url: String!, $token: String!) { - profile: page(url: $url, token: $token) { - ... on ProfileProfilePage { - username - firstName - lastName - email - verified - platformData - sources - bids - tids - } - } - } -`; -//#endregion - -//#region > Exports -export { whoami, profile }; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/tasks/auth.ts b/src/templates/snek/gql/tasks/auth.ts deleted file mode 100644 index f0f8eb5..0000000 --- a/src/templates/snek/gql/tasks/auth.ts +++ /dev/null @@ -1,144 +0,0 @@ -//#region > Imports -//> Parent Task -// Contains the SNEK parent task -import SnekTasks from "./index"; -//> Interfaces -// Contains the user interface for authentication -import { User } from "../../../../session"; -//> Types -// Contains the type declarations for Apollo results -import { ApolloResult } from "./index"; -//#endregion - -//#region > Interfaces -/** - * @interface AuthData defines the structure of the authentication result data - */ -interface AuthData { - auth: { - token: string; - refreshToken: string; - user: { - username: string; - }; - }; -} - -/** - * @interface RefreshData defines the structure of the refresh result data - */ -interface RefreshData { - refresh: { - payload: string; - token: string; - refreshToken: string; - }; -} - -/** - * @interface RevokeData defines the structure of the revoke result data - */ -interface RevokeData { - revoke: { - revoked: string; - }; -} -//#endregion - -//#region > Classes -/** @class A set of session aware tasks */ -class SnekGqlAuthTasks { - /** - * Initializes session aware SnekGqlAuth Task. - * - * @constructor - * @author Nico Schett - * @param {string} session A session for the tasks - */ - constructor(private parent: SnekTasks) {} - - /** - * Anonymous login. - * - * @returns {Promise>} Authentication data - * @description Authenticates the anonymous user to obtain a JWT - */ - async anon(): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.jwtAuth.auth, - { - username: "cisco", - password: "ciscocisco", - } - ); - - return response; - } - - /** - * User login. - * - * @param {string} user A User defined by username and password - * @returns {Promise>} Authentication data - * @description Authenticates a real user to obtain a JWT - */ - async nonanon(user: User): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.jwtAuth.auth, - { - username: user.username, - password: user.password, - } - ); - - return response; - } - - /** - * Refresh a token. - * - * @param {string} user A User defined by username and password - * @returns {Promise>} Refresh data - */ - async refresh(): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.jwtAuth.refresh, - { - refreshToken: this.parent.session.refreshToken, - } - ); - - return response; - } - - /** - * Revoke a token. - * - * @param {string} user A User defined by username and password - * @returns {Promise>} Revoke acknowledgment - */ - async revoke(): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.jwtAuth.revoke, - { - refreshToken: this.parent.session.refreshToken, - } - ); - - return response; - } -} -//#endregion - -//#region > Exports -export default SnekGqlAuthTasks; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/tasks/general.ts b/src/templates/snek/gql/tasks/general.ts deleted file mode 100644 index 6efc0c0..0000000 --- a/src/templates/snek/gql/tasks/general.ts +++ /dev/null @@ -1,86 +0,0 @@ -//#region > Imports -//> Parent Task -// Contains the SNEK parent task -import SnekTasks from "./index"; -//> Types -// Contains the type declarations for Apollo results -import { ApolloResult } from "./index"; -//#endregion - -//#region > Interfaces -/** - * @interface GitlabServerData defines the structure of the Gitlab server result - * data. - */ -interface GitlabServerData { - page: { - supportedGitlabs: []; - }; -} - -/** - * @interface AllPageUrlData defines the structure of the all page url result - * data. - */ -interface AllPageUrlData { - pages: []; -} -//#endregion - -//#region > Classes -/** @class A set of session aware tasks */ -class SnekGqlGeneralTasks { - /** - * Initializes SnekGqlGeneral tasks. - * @constructor - * @author Nico Schett - * @param {string} parent The parent task - */ - constructor(private parent: SnekTasks) {} - - /** - * Gitlab Server. - * - * @returns {Promise>} A list of Gitlab server - * @description Get all Gitlab servers which are registered in the SNEK-engine - */ - async gitlabServer(): Promise> { - const response = await this.parent.run( - "query", - this.parent.template.queries.general.gitlabServer, - { - token: await this.parent.session.upToDateToken(), - } - ); - - return response; - } - - /** - * All page url. - * - * @returns {Promise>} A list of all page urls - * @description Get a list of all pages - */ - async allPageUrls(): Promise> { - const response = await this.parent.run( - "query", - this.parent.template.queries.general.allPageUrls, - { - token: await this.parent.session.upToDateToken(), - } - ); - - return response; - } -} -//#endregion - -//#region > Exports -export default SnekGqlGeneralTasks; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/tasks/index.ts b/src/templates/snek/gql/tasks/index.ts deleted file mode 100644 index ae0c713..0000000 --- a/src/templates/snek/gql/tasks/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -//#region > Imports -//#PACKAGE "graphql" -//## npm install "graphql"@14.6.0 -// Contains the interface for gql queries, mutations and subscriptions -import { DocumentNode } from "graphql"; - -//> Sessions -// Contains the SNEK session -import { SnekSession } from "../../../../session/sessions"; -//> Tasks -// Contains the SnekGqlAuth task -import SnekGqlAuthTasks from "./auth"; -// Contains the SnekGqlGeneral task -import SnekGqlGeneralTasks from "./general"; -// Contains the SnekGqlUser task -import SnekGqlUserTasks from "./user"; -// Contains error handling for tasks -import { TaskError } from "../errors"; -//> Interfaces -// Contains the snek template -import { SnekGqlTemplate } from "../index"; -//> Types -// Contains the type declarations for Apollo results -import { ApolloResult } from "../index"; -//#endregion - -//#region > Classes -/** @class A Template with initializes all tasks */ -class SnekTasks extends TaskError { - public template!: SnekGqlTemplate; - public general!: SnekGqlGeneralTasks; - public auth!: SnekGqlAuthTasks; - public user!: SnekGqlUserTasks; - - /** - * Initializes a SnekSession. - * - * @constructor - * @author Nico Schett - * @param {string} session A session to initialize all corresponding tasks - */ - constructor(session: SnekSession) { - super(session); - - this.template = session.template.snekGql; - - /* Init tasks */ - this.general = new SnekGqlGeneralTasks(this); - this.auth = new SnekGqlAuthTasks(this); - this.user = new SnekGqlUserTasks(this); - } - - async run(type: string, query: DocumentNode, variables: object) { - let response: ApolloResult; - - if (type === "query") { - response = await this.session.ep.sendQuery(query, variables); - } else if (type === "mutation") { - response = await this.session.ep.sendMutation(query, variables); - } else { - console.warn( - "No query type specified! Please re-check." + - "Selecting type 'query' as default" - ); - - response = await this.session.ep.sendQuery(query, variables); - } - - this.handleErrors(response); - - return response; - } -} -//#endregion - -//#region > Exports -export type { ApolloResult }; -export default SnekTasks; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/tasks/user.ts b/src/templates/snek/gql/tasks/user.ts deleted file mode 100644 index 274ee7e..0000000 --- a/src/templates/snek/gql/tasks/user.ts +++ /dev/null @@ -1,151 +0,0 @@ -//#region > Imports -//> Parent Task -// Contains the SNEK parent task -import SnekTasks from "./index"; -//> Types -// Contains the type declarations for Apollo results -import { ApolloResult } from "./index"; -//#endregion - -//#region > Interfaces -/** - * @interface RegistrationData defines the structure of the registration result - * data. - */ -interface RegistrationData { - result: string; -} - -/** - * @interface CacheData defines the structure of the cache result - * data. - */ -interface CacheData { - user: { - id: number; - }; -} - -/** - * @interface ProfileData defines the structure of the profile result - * data. - */ -interface ProfileData { - profile: { - username: string; - firstName: string; - lastName: string; - email: string; - verified: string; - platformData: string; - sources: string; - bids: string; - tids: string; - }; -} - -/** - * @interface WhoamiData defines the structure of the whoami result - * data. - */ -interface WhoamiData { - whoami: { - username: string; - }; -} -//#endregion - -//#region > Classes -/** @class A set of session aware tasks */ -class SnekGqlUserTasks { - /** - * @constructor - * @author Nico Schett - * @param {string} parent The parent task - */ - constructor(private parent: SnekTasks) {} - - /** - * Register a user. - * - * @param {object} values Registration parameters like username, email,... - * @returns {Promise>} Registration data - */ - async registration(values: object): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.user.registration, - { - token: await this.parent.session.upToDateToken(), - values, - } - ); - - return response; - } - - /** - * Cache a user. - * - * @param {string} platformData A serialized JSON object to be cached - * @returns {Promise>} Cache data - */ - async cache(platformData: string): Promise> { - const response = await this.parent.run( - "mutation", - this.parent.template.mutations.user.cache, - { - token: await this.parent.session.upToDateToken(), - platformData, - } - ); - - return response; - } - - /** - * Get profile. - * - * @param {string} url A url of a page - * @returns {Promise>} The profile page of a user - */ - async profile(url: string): Promise> { - const response = await this.parent.run( - "query", - this.parent.template.queries.user.profile, - { - url, - token: await this.parent.session.upToDateToken(), - } - ); - - return response; - } - - /** - * Whoami check. - * - * @returns {Promise>} User data - */ - async whoami(): Promise> { - const response = await this.parent.run( - "query", - this.parent.template.queries.user.whoami, - { - token: await this.parent.session.upToDateToken(), - } - ); - - return response; - } -} -//#endregion - -//#region > Exports -export default SnekGqlUserTasks; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/index.ts b/src/templates/snek/index.ts deleted file mode 100644 index 22e195a..0000000 --- a/src/templates/snek/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -//#region > Imports -//> SNEK Gql Tools -import SnekGql from "./gql"; -//#endregion - -//#region > Classes -/** @class A Template which initializes all SubTemplates */ -class SnekTemplate { - public snekGql = new SnekGql(); -} -//#endregion - -//#region > Exports -export default SnekTemplate; -//#endregion - -/** - * SPDX-License-Identifier: (EUPL-1.2) - * Copyright © Simon Prast - */ diff --git a/src/templates/snek/gql/mutations/jwtAuth.ts b/src/templates/snek/mutations/auth.ts similarity index 95% rename from src/templates/snek/gql/mutations/jwtAuth.ts rename to src/templates/snek/mutations/auth.ts index aad63d6..f1c2309 100644 --- a/src/templates/snek/gql/mutations/jwtAuth.ts +++ b/src/templates/snek/mutations/auth.ts @@ -21,11 +21,6 @@ const auth = gql` refreshToken user { username - firstName - lastName - email - dateJoined - lastLogin } } } diff --git a/src/templates/snek/queries/user.ts b/src/templates/snek/queries/user.ts new file mode 100644 index 0000000..b8fd6d7 --- /dev/null +++ b/src/templates/snek/queries/user.ts @@ -0,0 +1,26 @@ +//#region > Imports +//#PACKAGE "graphql-tag" +// Contains a gql tag for wrapping queries +import gql from "graphql-tag"; +//#endregion + +//#region > Queries +/** + * Whoami. + * + * @param {string} token A users JWT + * @returns {string} A username + * @description A query to fetch the username of the according token + */ +const whoami = gql` + query whoami($token: String!) { + whoami: me(token: $token) { + username + } + } +`; +//#endregion + +//#region > Exports +export { whoami }; +//#endregion diff --git a/src/templates/snek/tasks/auth.ts b/src/templates/snek/tasks/auth.ts new file mode 100644 index 0000000..bdd8bdc --- /dev/null +++ b/src/templates/snek/tasks/auth.ts @@ -0,0 +1,114 @@ +//#region > Imports +//> Parent Task +// Contains the SNEK parent task +import MainTask from "./index"; +//> Types +// Contains the graphql response types +import { + User, + ApolloResult, + GraphQLAuthentication, + GraphQLRefresh, + GraphQLRevoke, +} from "../types"; +//> Config +import Config from "../../../config.json"; +//#endregion + +//#region > Classes +/** @class A set of session aware tasks */ +class AuthTask { + /** + * Initializes session aware SnekGqlAuth Task. + * + * @constructor + * @author Nico Schett + * @param {string} session A session for the tasks + */ + constructor(private parent: MainTask) {} + + /** + * Anonymous login. + * + * @returns {Promise>} Authentication data + * @description Authenticates the anonymous user to obtain a JWT + */ + async anon(): Promise> { + const response = await this.parent.run( + "mutation", + this.parent.mutations.jwtAuth.auth, + { + username: Config.anonUser.username, + password: Config.anonUser.password, + } + ); + + return response; + } + + /** + * User login. + * + * @param {string} user A User defined by username and password + * @returns {Promise>} Authentication data + * @description Authenticates a real user to obtain a JWT + */ + async nonanon(user: User): Promise> { + const response = await this.parent.run( + "mutation", + this.parent.mutations.jwtAuth.auth, + { + username: user.username, + password: user.password, + } + ); + + return response; + } + + /** + * Refresh a token. + * + * @param {string} user A User defined by username and password + * @returns {Promise>} Refresh data + */ + async refresh(): Promise> { + const response = await this.parent.run( + "mutation", + this.parent.mutations.jwtAuth.refresh, + { + refreshToken: this.parent.session.refreshToken, + } + ); + + return response; + } + + /** + * Revoke a token. + * + * @param {string} user A User defined by username and password + * @returns {Promise>} Revoke acknowledgment + */ + async revoke(): Promise> { + const response = await this.parent.run( + "mutation", + this.parent.mutations.jwtAuth.revoke, + { + refreshToken: this.parent.session.refreshToken, + } + ); + + return response; + } +} +//#endregion + +//#region > Exports +export default AuthTask; +//#endregion + +/** + * SPDX-License-Identifier: (EUPL-1.2) + * Copyright © Simon Prast + */ diff --git a/src/templates/snek/tasks/index.ts b/src/templates/snek/tasks/index.ts new file mode 100644 index 0000000..ce030b8 --- /dev/null +++ b/src/templates/snek/tasks/index.ts @@ -0,0 +1,60 @@ +//#region > Imports +//#PACKAGE "graphql" +// Contains the interface for gql queries, mutations and subscriptions +import { DocumentNode } from "graphql"; + +//> Mutations +import * as mutationsAuth from "../mutations/auth"; +//> Queries +import * as queriesUser from "../queries/user"; +//> Types +// Contains the graphql response types +import { ApolloResult, TaskTypes } from "../types"; +// Contains error handling for tasks +import { TaskError } from "../errors"; +import AuthTask from "./auth"; +import UserTask from "./user"; +//#endregion + +//#region > Classes +class MainTask extends TaskError { + public mutations: { jwtAuth: typeof mutationsAuth } = { + jwtAuth: require("../mutations/auth"), + }; + public queries: { user: typeof queriesUser } = { + user: require("../queries/user"), + }; + public set = { + auth: new AuthTask(this), + user: new UserTask(this), + }; + + async run( + type: TaskTypes, + query: DocumentNode, + variables: { [key: string]: any } + ): Promise> { + let response: ApolloResult; + + if (type === "query") { + response = await this.session.ep.sendQuery(query, variables); + } else if (type === "mutation") { + response = await this.session.ep.sendMutation(query, variables); + } else { + console.warn( + "No query type specified! Please re-check." + + "Selecting type 'query' as default" + ); + response = await this.session.ep.sendQuery(query, variables); + } + + this.handleErrors(response); + + return response; + } +} +//#endregion + +//#region > Exports +export default MainTask; +//#endregion diff --git a/src/templates/snek/tasks/user.ts b/src/templates/snek/tasks/user.ts new file mode 100644 index 0000000..d1ec78e --- /dev/null +++ b/src/templates/snek/tasks/user.ts @@ -0,0 +1,48 @@ +//#region > Imports +//> Parent Task +// Contains the SNEK parent task +import MainTask from "./index"; +//> Types +// Contains the graphql response types +import { ApolloResult, GraphQLWhoami } from "../types"; +//#endregion + +//#region > Classes +/** @class A set of session aware tasks */ +class UserTasks { + /** + * Creates an instance of a SessionTasks. + * + * @constructor + * @author Nico Schett + * @param {string} session A session for the tasks + */ + constructor(private parent: MainTask) {} + + /** + * Whoami check + * + * @returns {Promise>} User data. + */ + async whoami(): Promise> { + const response = await this.parent.run( + "query", + this.parent.queries.user.whoami, + { + token: await this.parent.session.upToDateToken(), + } + ); + + return response; + } +} +//#endregion + +//#region > Exports +export default UserTasks; +//#endregion + +/** + * SPDX-License-Identifier: (EUPL-1.2) + * Copyright © Simon Prast + */ diff --git a/src/templates/snek/types.ts b/src/templates/snek/types.ts new file mode 100644 index 0000000..443543c --- /dev/null +++ b/src/templates/snek/types.ts @@ -0,0 +1,48 @@ +//#region > General Types +export { ApolloResult } from "../../endpoints/index"; +export { User } from "../../session"; + +export type TaskTypes = "query" | "mutation"; +//#endregion + +//#region > Graphql Response Types +//> Authentication +/** + * @interface GraphQLAuthentication defines the structure of the authentication + * result data. + */ +export interface GraphQLAuthentication { + auth: { + token: string; + refreshToken: string; + user: { + username: string; + }; + }; +} + +/** + * @interface GraphQLRefresh defines the structure of the refresh result data + */ +export interface GraphQLRefresh { + refresh: { + payload: string; + token: string; + refreshToken: string; + }; +} + +/** + * @interface GraphQLRevoke defines the structure of the revoke result data + */ +export interface GraphQLRevoke { + revoke: { + revoked: string; + }; +} + +/** @interface GraphQLWhoami defines the structure of a whoami data */ +export interface GraphQLWhoami { + whoami: { username: string }; +} +//#endregion diff --git a/tsconfig.json b/tsconfig.json index 9666c9b..8b61f29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,42 +1,70 @@ { "compilerOptions": { - "target": "ES2020", - /** - * Specify module code generation: 'none', 'commonjs', 'amd', 'system', - * 'umd', 'es2015', 'es2020', or 'ESNext'. - */ - "module": "commonjs", - /** - * Generates corresponding '.d.ts' file. - */ - "declaration": true, - /** - * Redirect output structure to the directory. - */ - "outDir": "lib", - /** - * Do not emit comments to output. - */ - "removeComments": true, - /** Enable all strict type-checking options. */ - "strict": true, - /** - * Enables emit interoperability between CommonJS and ES Modules - * via creation of namespace objects for all imports. - * Implies 'allowSyntheticDefaultImports'. - */ - "esModuleInterop": true, - /** - * Disallow inconsistently-cased references to the same file. - */ - "forceConsistentCasingInFileNames": true, - /** - * Allow json modules. - */ - "resolveJsonModule": true - }, - /** - * The files to not type check. - */ - "exclude": ["node_modules", "build", "lib"] + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + "resolveJsonModule": true, + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } }