Skip to content

Commit

Permalink
continue
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter committed Oct 19, 2023
1 parent 006c60b commit 6d7647f
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 44 deletions.
18 changes: 10 additions & 8 deletions deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"@processors/": "./source/processors/"
},
"tasks": {
"qq": "rm -rf .coverage && deno test --coverage=.coverage --unstable --trace-ops --allow-all source/metrics/engine/config_test.ts && deno coverage .coverage --exclude='(secret|log|io|errors|testing|validation).ts'",
"q": "rm -rf .coverage && deno test --coverage=.coverage --unstable --no-check --trace-ops --fail-fast --allow-all source/metrics && deno coverage .coverage",
"qq": "rm -rf .coverage && deno test --coverage=.coverage --no-check --unstable --trace-ops --allow-all source/metrics/engine/metadata_test.ts && deno coverage .coverage --exclude='(secret|log|io|errors|testing|validation|format|version|config|requests|processor|plugin|internal|component).ts'",
"q": "rm -rf .coverage && deno test --coverage=.coverage --unstable --trace-ops --fail-fast --allow-all source/metrics && deno coverage .coverage",
"btr": "deno run --allow-env --allow-read --allow-write=.btr.json --allow-run=deno --no-lock tasks.ts $0"
},
"btr-tasks": {
Expand Down Expand Up @@ -88,7 +88,7 @@
// Testing ==============================================================================
"test": {
"task": [
//"rm .coverage -rf",
"rm .coverage -rf",
"deno test source/metrics"
],
"description": "🧪 Run tests and collect coverage",
Expand All @@ -97,11 +97,13 @@
"traceOps": true,
//"parallel": true,
"shuffle": true,
//"coverage": ".coverage",
"coverage": ".coverage",
"failFast": 1,
"lock": false,
"unstable": true,
"permissions": {
"env": [
"all": true
/* "env": [
// Metrics
"METRICS_GITHUB_TOKEN",
"METRICS_GITHUB_APP_SECRET",
Expand Down Expand Up @@ -134,8 +136,8 @@
],
"run": [
// Cache
"$USERPROFILE/.astral/**/chrome.exe",
"$HOME/.astral/**/chrome",
"$USERPROFILE/.astral/**\/chrome.exe",
"$HOME/.astral/**\/chrome",
// Puppeteer
"/usr/bin/chromium-browser"
],
Expand All @@ -153,7 +155,7 @@
"api.github.com/emojis",
// processors/render.twemojis
"cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets"
]
]*/
}
},
"test:cache": {
Expand Down
4 changes: 2 additions & 2 deletions source/metrics/engine/components/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export abstract class Component extends Internal {
this.log.info("execution started")
await Component.state.parseAsync(state)
this.log.trace(this.context)
let result = null, error = null, recoverable = true
let result = undefined, error = null, recoverable = true
for (let attempt = 1; attempt <= (this.context.retries?.attempts ?? 1); attempt++) {
if (attempt > 1) {
this.log.message(`attempt ${attempt} of ${this.context.retries.attempts}`)
Expand Down Expand Up @@ -118,7 +118,7 @@ export abstract class Component extends Internal {
/** Load component statically */
static async load(context: Record<PropertyKey, unknown> & { id: string }) {
const url = context.id.startsWith("https://") ? new URL(context.id) : toFileUrl(`${this.path}/${context.id}/mod.ts`)
const { default: Module } = await import(url.href)
const { default: Module } = await import(url.href).catch(() => ({}))
if (!Module) {
throws(`${this.name} ${context.id} could not be loaded`)
}
Expand Down
4 changes: 2 additions & 2 deletions source/metrics/engine/components/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ export abstract class Plugin extends Component {
}

/** Run component statically */
static run({ tracker, state, context }: { tracker?: string; state: state; context: Record<PropertyKey, unknown> }) {
static async run({ tracker, state, context }: { tracker?: string; state: state; context: Record<PropertyKey, unknown> }) {
if (tracker) {
Object.defineProperties(context, { [Component.tracker]: { enumerable: false, value: tracker } })
}
if (!context.id) {
return new Plugin.NOP(context).run(state)
}
return super.run({ state, context: context as typeof context & { id: string } })
return await super.run({ state, context: context as typeof context & { id: string } }) as unknown as ReturnType<Plugin["run"]>
}

/** Load component statically */
Expand Down
4 changes: 2 additions & 2 deletions source/metrics/engine/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//Imports
import { is, Plugin } from "@engine/components/plugin.ts"
import { Processor } from "@engine/components/processor.ts"
import { cli, server, webrequest } from "@metrics/config.ts"
import { version } from "@metrics/version.ts"
import { cli, server, webrequest } from "@engine/config.ts"
import { version } from "@engine/version.ts"

/** Metadata */
export async function metadata() {
Expand Down
10 changes: 10 additions & 0 deletions source/metrics/engine/metadata_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect } from "@utils/testing.ts"
import { metadata } from "@engine/metadata.ts"
import { version } from "@engine/version.ts"

// TODO(@lowlighter): change to `[dir.source]` after https://github.com/denoland/deno_std/pull/3692
Deno.test("metadata()", { permissions: { read: true } }, async (t) => {
await t.step("returns metadata", async () => {
await expect(metadata()).to.eventually.containSubset({ version }).and.include.keys("plugins", "processors", "cli", "server", "webrequest")
})
})
2 changes: 1 addition & 1 deletion source/metrics/engine/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function process(_config: Record<PropertyKey, unknown>) {
}
await Promise.all([...pending])
const { result: _result } = await Plugin.run({ tracker, context: plugin, state })
result = _result as typeof result //TODO(@lowlighter): Fix type
result = _result
pending.clear()
}
const results = await Promise.all([...pending]) as Array<{ result: typeof state.result }>
Expand Down
14 changes: 14 additions & 0 deletions source/metrics/engine/process_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { expect, MetricsError } from "@utils/testing.ts"
import { process } from "@engine/process.ts"
import * as dir from "@engine/paths.ts"

Deno.test("process()", { permissions: { read: [dir.source] } }, async (t) => {
await t.step("returns processed config", async () => {
await expect(process({})).to.eventually.be.null
await expect(process({ plugins: [{ logs: "none" }] })).to.eventually.have.property("result")
await expect(process({ plugins: [{ id: "introduction", handle: "octocat", logs: "none", mock: true }] })).to.eventually.have.property("result")
})
await t.step("throws on unknown component", async () => {
await expect(process({ plugins: [{ id: "foo", logs: "none" }] })).to.rejectedWith(MetricsError, /could not be loaded/i)
})
})
10 changes: 6 additions & 4 deletions source/run/cli/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { env } from "@utils/io.ts"

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import env.
import core from "y/@actions/[email protected]"

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import core.

type compat = any

function deprecated(message: string) {
console.warn()
}
Expand All @@ -12,17 +14,17 @@ const to = {
},
}

export function compat(inputs: any) {
const config = { plugins: [] } as any
export function compat(inputs: compat) {
const config = { plugins: [] } as compat

/*-
- ❗ `plugin_introduction: yes` ➡️ `plugins: [{id: introduction}]`
- ❌ `` ➡️ `processors: [{id: inject.style, args: {style: ".introduction .title { display: none }"}}]`
*/
// 🙋 Introduction
if (to.boolean(inputs.plugin_introduction)) {
const plugin = { introduction: {} } as any
deprecated(`<plugin_introduction> is deprecated, use <introduction> plugin`)
const plugin = { introduction: {} } as compat
deprecated(`"plugin_introduction" is deprecated, use <introduction> plugin`)

deprecated(`<plugin_introduction_title> is deprecated, use <calendar> plugin with <calendar.range: ${duration}>`)
const title = to.boolean(inputs.plugin_introduction_title)
Expand Down
12 changes: 6 additions & 6 deletions source/run/cli/mod.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//Imports
import { Internal, is, toSchema } from "@metrics/components/internal.ts"
import { cli as schema, load } from "@metrics/config.ts"
import { Internal, is, toSchema } from "@engine/components/internal.ts"
import { cli as schema, load } from "@engine/config.ts"
import github from "y/@actions/[email protected]"
import { latest, version } from "@metrics/version.ts"
import { latest, version } from "@engine/version.ts"
import core from "y/@actions/[email protected]"
import { process } from "@metrics/process.ts"
import { process } from "@engine/process.ts"
import { parse } from "std/flags/mod.ts"
import { cyan, gray } from "std/fmt/colors.ts"
import { expandGlobSync } from "std/fs/expand_glob.ts"
Expand Down Expand Up @@ -33,8 +33,8 @@ class CLI extends Internal {
for (const [key, value] of Object.entries(inputs)) {
env.set(`INPUT_${key.replace(/ /g, "_").toUpperCase()}`, `${value}`)
}
console.log(compat(inputs))

Check warning

Code scanning / CodeQL

Use of returnless function Warning

the
function compat
does not return anything, yet the return value is used.
}
console.log(compat())
}

/** Run metrics */
Expand All @@ -49,7 +49,7 @@ class CLI extends Internal {
}
if (this.context.check_updates) {
const upstream = await latest()
if (version !== upstream) {
if (version.number !== upstream) {
core.info(`Version ${upstream} is available!`)
}
}
Expand Down
37 changes: 18 additions & 19 deletions source/run/server/mod.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
//Imports
import { serveListener } from "std/http/server.ts"
import { server as schema, webrequest } from "@metrics/config.ts"
import { process } from "@metrics/process.ts"
import { server as schema, webrequest } from "@engine/config.ts"
import { process } from "@engine/process.ts"
import { is } from "@utils/validation.ts"
import { env, KV, listen, read } from "@utils/io.ts"
import { Internal } from "@metrics/components/internal.ts"
import { Internal } from "@engine/components/internal.ts"
import * as YAML from "std/yaml/mod.ts"
import { parseHandle } from "@utils/parse.ts"
import { parseHandle } from "@utils/github.ts"
import * as Base64 from "std/encoding/base64.ts"
import { serveDir, serveFile } from "std/http/file_server.ts"
import { fromFileUrl } from "std/path/from_file_url.ts"
import { Status } from "std/http/http_status.ts"
import { metadata } from "@metrics/metadata.ts"
import { metadata } from "@engine/metadata.ts"
import { deferred } from "std/async/deferred.ts"
import { deepMerge } from "std/collections/deep_merge.ts"
import { getCookies, setCookie } from "std/http/cookie.ts"
import { formatValidationError } from "@utils/errors.ts"
import { Secret } from "@utils/secret.ts"
import { Requests } from "@metrics/components/requests.ts"
import { Requests } from "@engine/components/requests.ts"
import { App } from "y/@octokit/[email protected]"
import { client } from "./mod_imports.ts"
try {
Expand Down Expand Up @@ -72,7 +71,7 @@ class Server extends Internal {
/** Start server */
start() {
this.log.info(`listening on ${this.context.hostname}:${this.context.port}`)
return serveListener(listen({ hostname: this.context.hostname, port: this.context.port }), (request, connection) => this.handle(request, connection))
return serveListener(listen({ hostname: this.context.hostname, port: this.context.port }), (request, connection) => this.handle(request, connection as { remoteAddr: { hostname: string } }))
}

/** Metadata */
Expand Down Expand Up @@ -138,7 +137,7 @@ class Server extends Internal {
if (session && (await this.#kv.has(`sessions.${session}`))) {
return Response.redirect(url.origin, Status.Found)
}
return Response.redirect(this.#app.oauth.getWebFlowAuthorizationUrl({ allowSignup: false }).url, Status.Found)
return Response.redirect(this.#app!.oauth.getWebFlowAuthorizationUrl({ allowSignup: false }).url, Status.Found)
}
// OAuth authorization process
case "/authorize": {
Expand All @@ -148,9 +147,9 @@ class Server extends Internal {
try {
const state = url.searchParams.get("state")
const code = url.searchParams.get("code")
const { authentication: { token, expiresAt: expiration } } = await this.#app.oauth.createToken({ state, code })
const { authentication: { token, expiresAt: expiration } } = await this.#app!.oauth.createToken({ state, code })
const ttl = new Date(expiration).getTime() - Date.now()
const { data: { user: { login, avatar_url: avatar } } } = await this.#app.oauth.checkToken({ token })
const { data: { user: { login, avatar_url: avatar } } } = await this.#app!.oauth.checkToken({ token })
if (await this.#kv.has(`sessions.login.${login}`)) {
log.trace(`oauth process: user ${login} was already authenticated`)
return Response.redirect(url.origin, Status.Found)
Expand All @@ -174,7 +173,7 @@ class Server extends Internal {
}
try {
const { login, token } = await this.#kv.get<user>(`sessions.${session}`)
await this.#app.oauth.deleteToken({ token })
await this.#app!.oauth.deleteToken({ token })
await this.#kv.delete(`sessions.${session}`)
await this.#kv.delete(`sessions.login.${login}`)
log.trace(`oauth process: user ${login} revoked their token`)
Expand Down Expand Up @@ -205,7 +204,7 @@ class Server extends Internal {
// Serve renders
case this.routes.metrics.test(url.pathname): {
const { handle: _handle, ext = "svg" } = url.pathname.match(this.routes.metrics)?.groups ?? {}
const { handle, login } = parseHandle(_handle)
const { login: handle } = parseHandle(_handle, { entity: "user" })
const _log = log
let user = null as user | null
if (!handle) {
Expand Down Expand Up @@ -240,7 +239,7 @@ class Server extends Internal {
log.trace(context)
} catch (error) {
log.warn(error)
return promise.resolve(new Response(`Bad request: ${formatValidationError(error)}`, { status: Status.BadRequest }))!
return promise.resolve(new Response(`Bad request: ${error}`, { status: Status.BadRequest }))!
}

// Filter features and apply server configuration
Expand All @@ -253,7 +252,7 @@ class Server extends Internal {
//TODO(@lowlighter): filter enabled plugins and params
} catch (error) {
log.warn(error)
return promise.resolve(new Response(`Bad request: ${formatValidationError(error)}`, { status: Status.BadRequest }))!
return promise.resolve(new Response(`Bad request: ${error}`, { status: Status.BadRequest }))!
}

// Load user session if available
Expand Down Expand Up @@ -283,13 +282,13 @@ class Server extends Internal {
await this.#kv.get<{ current: number; reset: number; init?: boolean }>(`requests.ratelimit.${user}`) ?? { init: true }
log.trace(`requests ratelimit: ${current + 1} / ${limit} until ${new Date(reset).toISOString()}`)
if (current + 1 > limit) {
log.trace(`requests ratelimit: preventing further requests for "${login}" until ${new Date(reset).toISOString()}`)
log.trace(`requests ratelimit: preventing further requests for "${from}" until ${new Date(reset).toISOString()}`)
return new Response(`Too Many Requests: Rate limit exceeded (wait ~${Math.ceil(Math.max((reset - Date.now()) / 1000, 1))}s)`, { status: Status.TooManyRequests })
}
if (init) {
await this.#kv.set(`requests.ratelimit.${login}`, { current: current + 1, reset }, { ttl: 1000 * duration })
await this.#kv.set(`requests.ratelimit.${from}`, { current: current + 1, reset }, { ttl: 1000 * duration })
} else {
await this.#kv.set(`requests.ratelimit.${login}`, { current: current + 1, reset })
await this.#kv.set(`requests.ratelimit.${from}`, { current: current + 1, reset })
}
}
}
Expand Down Expand Up @@ -358,7 +357,7 @@ class Server extends Internal {
switch (action) {
// Stop instance
case "stop": {
if (!this.context.control[token]?.[action]) {
if (!this.context.control?.[token]?.[action]) {
return new Response("Forbidden", { status: Status.Forbidden })
}
this.log.info("received stop request, server will shutdown in a few seconds")
Expand Down

0 comments on commit 6d7647f

Please sign in to comment.