From a4e127a6ddd5560e5697cd89cb6bcf8b5d7402bb Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Fri, 29 Apr 2022 13:37:31 +0200 Subject: [PATCH] Clean --- .gitignore | 2 + package-lock.json | 5 +- package.json | 5 +- packages/plugs/github/github.plug.yaml | 6 +- packages/plugs/github/github.ts | 110 ++++++++++++++++++++----- packages/plugs/package.json | 3 +- packages/plugs/query/util.ts | 63 ++++++++++++-- 7 files changed, 156 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index ef1bf0e0..c8ca5cca 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ node_modules dist generated .yarnrc.yml +*.test.js +*.js.map \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 79dd9abb..ad7e05cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,6 @@ "workspaces": [ "packages/*" ], - "dependencies": { - "handlebars": "^4.7.7" - }, "devDependencies": { "@parcel/core": "2.3.2", "@parcel/packager-raw-url": "2.3.2", @@ -9930,6 +9927,7 @@ "@mattermost/client": "^6.7.0-0", "@mattermost/types": "^6.7.0-0", "@types/yaml": "^1.9.7", + "handlebars": "^4.7.7", "markdown-it": "^12.3.2", "markdown-it-task-lists": "^2.1.1", "yaml": "^2.0.0" @@ -11526,6 +11524,7 @@ "@mattermost/types": "^6.7.0-0", "@types/markdown-it": "^12.2.3", "@types/yaml": "^1.9.7", + "handlebars": "^4.7.7", "markdown-it": "^12.3.2", "markdown-it-task-lists": "^2.1.1", "yaml": "^2.0.0" diff --git a/package.json b/package.json index d790bf7b..8a337fdd 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,5 @@ }, "workspaces": [ "packages/*" - ], - "dependencies": { - "handlebars": "^4.7.7" - } + ] } diff --git a/packages/plugs/github/github.plug.yaml b/packages/plugs/github/github.plug.yaml index c27957fb..b8b20e99 100644 --- a/packages/plugs/github/github.plug.yaml +++ b/packages/plugs/github/github.plug.yaml @@ -1,6 +1,10 @@ name: github functions: - test: + queryEvents: path: ./github.ts:queryEvents events: - query:gh-events + queryIssues: + path: ./github.ts:queryIssues + events: + - query:gh-issues diff --git a/packages/plugs/github/github.ts b/packages/plugs/github/github.ts index 7bec413d..a8a29ac2 100644 --- a/packages/plugs/github/github.ts +++ b/packages/plugs/github/github.ts @@ -1,5 +1,7 @@ import { applyQuery, QueryProviderEvent, renderQuery } from "../query/engine"; -import { jsonToMDTable } from "../query/util"; +import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space"; +import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; +import { extractMeta } from "../query/data"; type GithubEvent = { id: string; @@ -37,16 +39,53 @@ type ExposedEvent = { repo: string; }; -async function listEvents(username: string): Promise { - let events = await fetch(`https://api.github.com/users/${username}/events`); - return await events.json(); +class GithubApi { + constructor(private token?: string) {} + + async apiCall(url: string, options: any = {}): Promise { + let res = await fetch(url, { + ...options, + headers: { + Authorization: this.token ? `token ${this.token}` : undefined, + }, + }); + if (res.status !== 200) { + throw new Error(await res.text()); + } + return res.json(); + } + + async listEvents(username: string): Promise { + return this.apiCall( + `https://api.github.com/users/${username}/events?per_page=100` + ); + } + + async listIssues(filter: string): Promise { + return this.apiCall( + `https://api.github.com/issues?q=${encodeURIComponent(filter)}` + ); + } + + static async fromConfig(): Promise { + return new GithubApi((await getConfig()).token); + } } -async function listIssues(filter: string): Promise { - let issues = await fetch( - `https://api.github.com/issues?q=${encodeURIComponent(filter)}` - ); - return await issues.json(); +type GithubConfig = { + token?: string; +}; + +async function getConfig(): Promise { + try { + let { text } = await readPage("github-config"); + let parsedContent = await parseMarkdown(text); + let pageMeta = await extractMeta(parsedContent); + return pageMeta as GithubConfig; + } catch (e) { + console.error("No github-config page found, using default config"); + return {}; + } } function mapEvent(event: GithubEvent): any { @@ -64,21 +103,50 @@ function mapEvent(event: GithubEvent): any { export async function queryEvents({ query, }: QueryProviderEvent): Promise { + let api = await GithubApi.fromConfig(); let usernameFilter = query.filter.find((f) => f.prop === "username"); if (!usernameFilter) { throw Error("No 'username' filter specified, this is mandatory"); } - let username = usernameFilter.value; - let allEvents = (await listEvents(username)).map(mapEvent); - return applyQuery(query, allEvents); + let usernames: string[] = []; + if (usernameFilter.op === "=") { + usernames = [usernameFilter.value]; + } else if (usernameFilter.op === "in") { + usernames = usernameFilter.value; + } else { + throw new Error(`Unsupported operator ${usernameFilter.op}`); + } + let allEvents: GithubEvent[] = []; + for (let eventList of await Promise.all( + usernames.map((username) => api.listEvents(username)) + )) { + allEvents.push(...eventList); + } + // console.log("Usernames", usernames, "Event list lenght", allEvents[0]); + return applyQuery(query, allEvents.map(mapEvent)); } -// export async function queryIssues({ -// query, -// }: QueryProviderEvent): Promise { -// let filter = query.filter.find((f) => f.prop === "filter"); -// if (!filter) { -// throw Error("No 'filter' specified, this is mandatory"); -// } -// let username = filter.value; -// } +export async function queryIssues({ + query, +}: QueryProviderEvent): Promise { + let api = await GithubApi.fromConfig(); + let filter = query.filter.find((f) => f.prop === "filter"); + if (!filter) { + throw Error("No 'filter' specified, this is mandatory"); + } + let queries: string[] = []; + if (filter.op === "=") { + queries = [filter.value]; + } else if (filter.op === "in") { + queries = filter.value; + } else { + throw new Error(`Unsupported operator ${filter.op}`); + } + let allIssues: any[] = []; + for (let issuesList of await Promise.all( + queries.map((query) => api.listIssues(query)) + )) { + allIssues.push(...issuesList); + } + return allIssues; +} diff --git a/packages/plugs/package.json b/packages/plugs/package.json index f399fd67..fb54b12b 100644 --- a/packages/plugs/package.json +++ b/packages/plugs/package.json @@ -39,7 +39,8 @@ "@types/yaml": "^1.9.7", "markdown-it": "^12.3.2", "markdown-it-task-lists": "^2.1.1", - "yaml": "^2.0.0" + "yaml": "^2.0.0", + "handlebars": "^4.7.7" }, "devDependencies": { "@types/markdown-it": "^12.2.3" diff --git a/packages/plugs/query/util.ts b/packages/plugs/query/util.ts index f81f07a4..35a35aee 100644 --- a/packages/plugs/query/util.ts +++ b/packages/plugs/query/util.ts @@ -1,4 +1,9 @@ -import { addParentPointers, collectNodesMatching, ParseTree, renderToText } from "@silverbulletmd/common/tree"; +import { + addParentPointers, + collectNodesMatching, + ParseTree, + renderToText, +} from "@silverbulletmd/common/tree"; export const queryRegex = /()(.+?)()/gs; @@ -44,27 +49,69 @@ export function removeQueries(pt: ParseTree) { }); } +const maxWidth = 70; // Nicely format an array of JSON objects as a Markdown table export function jsonToMDTable( jsonArray: any[], - valueTransformer?: (k: string, v: any) => string | undefined + valueTransformer: (k: string, v: any) => string = (k, v) => "" + v ): string { - let headers = new Set(); + let fieldWidths = new Map(); for (let entry of jsonArray) { for (let k of Object.keys(entry)) { - headers.add(k); + let fieldWidth = fieldWidths.get(k); + if (!fieldWidth) { + fieldWidth = valueTransformer(k, entry[k]).length; + } else { + fieldWidth = Math.max(valueTransformer(k, entry[k]).length, fieldWidth); + } + fieldWidths.set(k, fieldWidth); } } - let headerList = [...headers]; + + let fullWidth = 0; + for (let v of fieldWidths.values()) { + fullWidth += v + 1; + } + + let headerList = [...fieldWidths.keys()]; let lines = []; - lines.push("|" + headerList.join("|") + "|"); - lines.push("|" + headerList.map((title) => "----").join("|") + "|"); + lines.push( + "|" + + headerList + .map( + (headerName) => + headerName + + charPad(" ", fieldWidths.get(headerName)! - headerName.length) + ) + .join("|") + + "|" + ); + lines.push( + "|" + + headerList + .map((title) => charPad("-", fieldWidths.get(title)!)) + .join("|") + + "|" + ); for (const val of jsonArray) { let el = []; for (let prop of headerList) { - el.push(valueTransformer ? valueTransformer(prop, val[prop]) : val[prop]); + let s = valueTransformer(prop, val[prop]); + el.push(s + charPad(" ", fieldWidths.get(prop)! - s.length)); } lines.push("|" + el.join("|") + "|"); } return lines.join("\n"); + + function charPad(ch: string, length: number) { + if (fullWidth > maxWidth && ch === "") { + return ""; + } else if (fullWidth > maxWidth && ch === "-") { + return "--"; + } + if (length < 1) { + return ""; + } + return new Array(length + 1).join(ch); + } }