mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 13:17:08 +00:00
Update minimum Node version to 22 (#990)
* Update dependencies * Node 22 is now the new minimum version. * changelog. * Begin porting eslint to new config format. * Make linter happy. * Update reqwest to fix SSL issue? * Fix test types * quick check on ubuntu LTS 24.04 * Change cache key * update rust action * revert mocha due to esminess * Remove the only usage of pqueue * Use babel for TS transformations to get around ESM import bug. * Dependency bundle upgrade * Drop babel, not actually used. * lint * lint * update default config (mostly sections moving around)
This commit is contained in:
parent
745699abd3
commit
819c089aa4
60
.eslintrc.js
60
.eslintrc.js
@ -1,60 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint'
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
// eslint-config-preact needs a Jest version to be happy, even if Jest isn't used.
|
||||
// See https://github.com/preactjs/eslint-config-preact/issues/19#issuecomment-997924892
|
||||
settings: {
|
||||
jest: { "version": 27 },
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"camelcase": ["error", { "properties": "never", "ignoreDestructuring": true }],
|
||||
"no-console": "error"
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["test/**/*.ts"],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'mocha',
|
||||
],
|
||||
},
|
||||
{
|
||||
files: ["web/**/*.ts", "web/**/*.tsx"],
|
||||
parser: '@typescript-eslint/parser',
|
||||
env: {
|
||||
browser: true,
|
||||
node: false,
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'preact',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": ["error"],
|
||||
"no-useless-constructor": "off",
|
||||
"@typescript-eslint/no-useless-constructor": ["error"],
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
@ -34,10 +34,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
- run: cargo clippy -- -Dwarnings
|
||||
@ -66,23 +65,22 @@ jobs:
|
||||
|
||||
test:
|
||||
# Test on LTS-1
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: [20, 21]
|
||||
node_version: [22, 23]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: rust-cache
|
||||
shared-key: ubuntu-2204-rust-cache
|
||||
- run: yarn
|
||||
- run: yarn test:cover
|
||||
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -13,6 +13,10 @@ public/
|
||||
# Generated during build
|
||||
/src/libRs.d.ts
|
||||
|
||||
# Generated during test run
|
||||
/spec-lib
|
||||
/hookshot-int-*
|
||||
|
||||
book
|
||||
*.cer
|
||||
*.pem
|
||||
|
@ -1 +1 @@
|
||||
20
|
||||
22
|
||||
|
326
Cargo.lock
generated
326
Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -51,6 +51,12 @@ dependencies = [
|
||||
"quick-xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -78,6 +84,12 @@ version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
@ -125,9 +137,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@ -456,15 +468,15 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
@ -507,9 +519,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.11"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -518,12 +530,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@ -533,47 +557,76 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -648,7 +701,7 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
"spin 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -717,7 +770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matrix-hookshot"
|
||||
version = "5.3.0"
|
||||
version = "5.4.1"
|
||||
dependencies = [
|
||||
"atom_syndication",
|
||||
"base64ct",
|
||||
@ -1265,11 +1318,11 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.24"
|
||||
version = "0.12.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
||||
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@ -1277,8 +1330,11 @@ dependencies = [
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
@ -1300,7 +1356,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1312,6 +1368,21 @@ dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.6"
|
||||
@ -1366,7 +1437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bca4c33c50e47b4cdceeac71bdef0c04153b0e29aa992d9030ec14a62323e85"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64",
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"indexmap",
|
||||
@ -1467,12 +1538,42 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
name = "rustls"
|
||||
version = "0.23.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1659,6 +1760,12 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
@ -1731,26 +1838,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -1840,6 +1950,17 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
@ -1964,6 +2085,12 @@ version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
@ -2090,6 +2217,36 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@ -2105,7 +2262,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2125,17 +2282,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2146,9 +2304,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@ -2158,9 +2316,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@ -2170,9 +2328,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@ -2182,9 +2346,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@ -2194,9 +2358,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@ -2206,9 +2370,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@ -2218,9 +2382,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
@ -2231,16 +2395,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.7.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "matrix-hookshot"
|
||||
version = "5.3.0"
|
||||
version = "5.4.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@ -20,7 +20,7 @@ hex = "0.4"
|
||||
rss = "2.0"
|
||||
atom_syndication = "0.12"
|
||||
ruma = { version = "0.9", features = ["events", "html"] }
|
||||
reqwest = "0.11"
|
||||
reqwest = "0.12.9"
|
||||
rand = "0.8.5"
|
||||
rsa = { version = "0.9.6", features = ["sha2"] }
|
||||
base64ct = { version = "1.6.0", features = ["alloc"] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Stage 0: Build the thing
|
||||
# Need debian based image to build the native rust module
|
||||
# as musl doesn't support cdylib
|
||||
FROM node:20.12-slim AS builder
|
||||
FROM node:22-slim AS builder
|
||||
|
||||
# Needed in order to build rust FFI bindings.
|
||||
RUN apt-get update && apt-get install -y build-essential cmake curl pkg-config pkg-config libssl-dev
|
||||
@ -29,7 +29,7 @@ RUN yarn build
|
||||
|
||||
|
||||
# Stage 1: The actual container
|
||||
FROM node:20.12-slim
|
||||
FROM node:22-slim
|
||||
|
||||
WORKDIR /bin/matrix-hookshot
|
||||
|
||||
|
1
changelog.d/990.removal
Normal file
1
changelog.d/990.removal
Normal file
@ -0,0 +1 @@
|
||||
Drop support for Node 20 and start supporting Node 22, 23.
|
@ -1,11 +1,5 @@
|
||||
# This is an example configuration file
|
||||
|
||||
logging:
|
||||
# Logging settings. You can have a severity debug,info,warn,error
|
||||
level: info
|
||||
colorize: true
|
||||
json: false
|
||||
timestampFormat: HH:mm:ss:SSS
|
||||
bridge:
|
||||
# Basic homeserver configuration
|
||||
domain: example.com
|
||||
@ -13,6 +7,12 @@ bridge:
|
||||
mediaUrl: https://example.com
|
||||
port: 9993
|
||||
bindAddress: 127.0.0.1
|
||||
logging:
|
||||
# Logging settings. You can have a severity debug,info,warn,error
|
||||
level: info
|
||||
colorize: true
|
||||
json: false
|
||||
timestampFormat: HH:mm:ss:SSS
|
||||
passFile:
|
||||
# A passkey used to encrypt tokens stored inside the bridge.
|
||||
# Run openssl genpkey -out passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096 to generate
|
||||
@ -37,6 +37,25 @@ listeners:
|
||||
resources:
|
||||
- widgets
|
||||
|
||||
#cache:
|
||||
# # (Optional) Cache options for large scale deployments.
|
||||
# # For encryption to work, this must be configured.
|
||||
# redisUri: redis://localhost:6379
|
||||
|
||||
#encryption:
|
||||
# # (Optional) Configuration for encryption support in the bridge.
|
||||
# # If omitted, encryption support will be disabled.
|
||||
# storagePath:
|
||||
# # Path to the directory used to store encryption files. These files must be persist between restarts of the service.
|
||||
# ./cryptostore
|
||||
|
||||
#permissions:
|
||||
# # (Optional) Permissions for using the bridge. See docs/setup.md#permissions for help
|
||||
# - actor: example.com
|
||||
# services:
|
||||
# - service: "*"
|
||||
# level: admin
|
||||
|
||||
#github:
|
||||
# # (Optional) Configure this to enable GitHub support
|
||||
# auth:
|
||||
@ -75,15 +94,6 @@ listeners:
|
||||
# # (Optional) Aggregate comments by waiting this many miliseconds before posting them to Matrix. Defaults to 5000 (5 seconds)
|
||||
# 5000
|
||||
|
||||
#figma:
|
||||
# # (Optional) Configure this to enable Figma support
|
||||
# publicUrl: https://example.com/hookshot/
|
||||
# instances:
|
||||
# your-instance:
|
||||
# teamId: your-team-id
|
||||
# accessToken: your-personal-access-token
|
||||
# passcode: your-webhook-passcode
|
||||
|
||||
#jira:
|
||||
# # (Optional) Configure this to enable Jira support. Only specify `url` if you are using a On Premise install (i.e. not atlassian.com)
|
||||
# webhook:
|
||||
@ -101,25 +111,30 @@ listeners:
|
||||
|
||||
# enabled: false
|
||||
# outbound: false
|
||||
# enableHttpGet: false
|
||||
# sendExpiryNotice: false
|
||||
# requireExpiryTime: false
|
||||
# urlPrefix: https://example.com/webhook/
|
||||
# userIdPrefix: _webhooks_
|
||||
# allowJsTransformationFunctions: false
|
||||
# waitForComplete: false
|
||||
# enableHttpGet: false
|
||||
# sendExpiryNotice: false
|
||||
# requireExpiryTime: false
|
||||
# maxExpiryTime: 30d
|
||||
|
||||
#figma:
|
||||
# # (Optional) Configure this to enable Figma support
|
||||
# publicUrl: https://example.com/hookshot/
|
||||
# instances:
|
||||
# your-instance:
|
||||
# teamId: your-team-id
|
||||
# accessToken: your-personal-access-token
|
||||
# passcode: your-webhook-passcode
|
||||
|
||||
#feeds:
|
||||
# # (Optional) Configure this to enable RSS/Atom feed support
|
||||
# enabled: false
|
||||
# pollConcurrency: 4
|
||||
# pollIntervalSeconds: 600
|
||||
# pollTimeoutSeconds: 30
|
||||
|
||||
#provisioning:
|
||||
# # (Optional) Provisioning API for integration managers
|
||||
# secret: "!secretToken"
|
||||
# pollConcurrency: 4
|
||||
|
||||
#bot:
|
||||
# # (Optional) Define profile information for the bot user
|
||||
@ -134,25 +149,12 @@ listeners:
|
||||
# prefix: "!feeds"
|
||||
# service: feeds
|
||||
|
||||
#metrics:
|
||||
# # (Optional) Prometheus metrics support
|
||||
# enabled: true
|
||||
|
||||
#cache:
|
||||
# # (Optional) Cache options for large scale deployments.
|
||||
# # For encryption to work, this must be configured.
|
||||
# redisUri: redis://localhost:6379
|
||||
|
||||
#encryption:
|
||||
# # (Optional) Configuration for encryption support in the bridge.
|
||||
# # If omitted, encryption support will be disabled.
|
||||
# storagePath:
|
||||
# # Path to the directory used to store encryption files. These files must be persist between restarts of the service.
|
||||
# ./cryptostore
|
||||
|
||||
#widgets:
|
||||
# # (Optional) EXPERIMENTAL support for complimentary widgets
|
||||
# addToAdminRooms: false
|
||||
# publicUrl: https://example.com/widgetapi/v1/static/
|
||||
# roomSetupWidget:
|
||||
# addOnInvite: false
|
||||
# disallowedIpRanges:
|
||||
# - 127.0.0.0/8
|
||||
# - 10.0.0.0/8
|
||||
@ -173,23 +175,21 @@ listeners:
|
||||
# - 2001:db8::/32
|
||||
# - ff00::/8
|
||||
# - fec0::/10
|
||||
# roomSetupWidget:
|
||||
# addOnInvite: false
|
||||
# publicUrl: https://example.com/widgetapi/v1/static/
|
||||
# branding:
|
||||
# widgetTitle: Hookshot Configuration
|
||||
|
||||
#provisioning:
|
||||
# # (Optional) Provisioning API for integration managers
|
||||
# secret: "!secretToken"
|
||||
|
||||
#metrics:
|
||||
# # (Optional) Prometheus metrics support
|
||||
# enabled: true
|
||||
|
||||
#sentry:
|
||||
# # (Optional) Configure Sentry error reporting
|
||||
# dsn: https://examplePublicKey@o0.ingest.sentry.io/0
|
||||
# environment: production
|
||||
|
||||
#permissions:
|
||||
# # (Optional) Permissions for using the bridge. See docs/setup.md#permissions for help
|
||||
# - actor: example.com
|
||||
# services:
|
||||
# - service: "*"
|
||||
# level: admin
|
||||
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ in
|
||||
languages.typescript.enable = true;
|
||||
languages.javascript.yarn.enable = true;
|
||||
languages.javascript.enable = true;
|
||||
languages.javascript.package = pkgs-upstream.nodejs_20;
|
||||
languages.javascript.package = pkgs-upstream.nodejs_22;
|
||||
languages.rust.enable = true;
|
||||
languages.rust.channel = "stable";
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ Hookshot requires the homeserver to be configured with its appservice registrati
|
||||
|
||||
## Local installation
|
||||
|
||||
This bridge requires at least Node 16 and Rust installed.
|
||||
This bridge requires at least Node 22 and Rust installed.
|
||||
|
||||
To install Node.JS, [nvm](https://github.com/nvm-sh/nvm) is a good option.
|
||||
|
||||
|
82
eslint.config.mjs
Normal file
82
eslint.config.mjs
Normal file
@ -0,0 +1,82 @@
|
||||
import mocha from "eslint-plugin-mocha";
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from "eslint-plugin-react";
|
||||
import chai from "eslint-plugin-chai-expect";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ["lib/**/*", "spec-lib/**/*", "contrib/**/*"],
|
||||
},
|
||||
...tseslint.config(
|
||||
{
|
||||
files: ["src/**/*.ts", "scripts/*.ts"],
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
camelcase: ["error", {
|
||||
properties: "never",
|
||||
ignoreDestructuring: true,
|
||||
}],
|
||||
"no-console": "error",
|
||||
},
|
||||
},
|
||||
),
|
||||
...tseslint.config(
|
||||
{
|
||||
files: ["tests/**/*.ts", "tests/**/*.spec.ts"],
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
mocha.configs.flat.recommended,
|
||||
chai.configs["recommended-flat"],
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
// Chai assertions don't call functions
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
camelcase: ["error", {
|
||||
properties: "never",
|
||||
ignoreDestructuring: true,
|
||||
}],
|
||||
"no-console": "error",
|
||||
// Needs a refactor
|
||||
"mocha/no-mocha-arrows": "off",
|
||||
},
|
||||
},
|
||||
),
|
||||
...tseslint.config(
|
||||
{
|
||||
settings: {
|
||||
react: {
|
||||
pragma: "Preact",
|
||||
version: "17",
|
||||
}
|
||||
},
|
||||
files: ["web/**/*.ts", "web/**/*.tsx"],
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
react.configs.flat.recommended,
|
||||
react.configs.flat['jsx-runtime'],
|
||||
],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-constructor": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-useless-constructor": "error",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/prop-types": "off",
|
||||
},
|
||||
},
|
||||
),
|
||||
];
|
@ -7,15 +7,8 @@ import type {Config} from 'jest';
|
||||
|
||||
const config: Config = {
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
rootDir: "spec",
|
||||
rootDir: "spec-lib",
|
||||
testTimeout: 60000,
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
60
package.json
60
package.json
@ -10,7 +10,7 @@
|
||||
"name": "matrix-hookshot-rs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=22"
|
||||
},
|
||||
"scripts": {
|
||||
"build:web": "vite build",
|
||||
@ -31,11 +31,11 @@
|
||||
"start:webhooks": "node --require source-map-support/register lib/App/GithubWebhookApp.js",
|
||||
"start:matrixsender": "node --require source-map-support/register lib/App/MatrixSenderApp.js",
|
||||
"start:resetcrypto": "node --require source-map-support/register lib/App/ResetCryptoStore.js",
|
||||
"test": "mocha -r ts-node/register tests/init.ts tests/*.ts tests/**/*.ts",
|
||||
"test:e2e": "yarn node --experimental-vm-modules $(yarn bin jest)",
|
||||
"test": "mocha -r ts-node/register tests/init.ts 'tests/*.ts' 'tests/**/*.ts'",
|
||||
"test:e2e": "tsc --p tsconfig.spec.json && cp ./lib/libRs.js ./lib/matrix-hookshot-rs.node ./spec-lib/src && yarn node --experimental-vm-modules $(yarn bin jest)",
|
||||
"test:cover": "nyc --reporter=lcov --reporter=text yarn test",
|
||||
"lint": "yarn run lint:js && yarn run lint:rs",
|
||||
"lint:js": "eslint -c .eslintrc.js 'src/**/*.ts' 'tests/**/*.ts' 'web/**/*.ts' 'web/**/*.tsx'",
|
||||
"lint:js": "eslint",
|
||||
"lint:rs": "cargo fmt --all -- --check && cargo clippy -- -Dwarnings",
|
||||
"lint:rs:apply": "cargo clippy --fix && cargo fmt --all",
|
||||
"generate-default-config": "ts-node src/config/Defaults.ts --config > config.sample.yml",
|
||||
@ -63,16 +63,15 @@
|
||||
"jira-client": "^8.2.2",
|
||||
"markdown-it": "^14.0.0",
|
||||
"matrix-appservice-bridge": "^9.0.1",
|
||||
"matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@v0.7.1-element.6",
|
||||
"matrix-widget-api": "^1.6.0",
|
||||
"matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@0.7.1-element.6",
|
||||
"matrix-widget-api": "^1.10.0",
|
||||
"micromatch": "^4.0.8",
|
||||
"mime": "^4.0.1",
|
||||
"mime": "^4.0.4",
|
||||
"node-emoji": "^2.1.3",
|
||||
"p-queue": "^6.6.2",
|
||||
"parse-duration": "^1.1.0",
|
||||
"preact-render-to-string": "^6.3.1",
|
||||
"prom-client": "^15.1.0",
|
||||
"quickjs-emscripten": "^0.26.0",
|
||||
"quickjs-emscripten": "^0.31.0",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"string-argv": "^0.3.1",
|
||||
@ -84,11 +83,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/lang-javascript": "^6.0.2",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.15.0",
|
||||
"@fontsource/inter": "^5.1.0",
|
||||
"@napi-rs/cli": "^2.13.2",
|
||||
"@preact/preset-vite": "^2.2.0",
|
||||
"@octokit/webhooks-types": "^7.6.1",
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@rollup/plugin-alias": "^5.1.0",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@tsconfig/node22": "^22",
|
||||
"@types/ajv": "^1.0.0",
|
||||
"@types/busboy": "^1.5.4",
|
||||
"@types/chai": "^4.2.22",
|
||||
@ -100,28 +102,28 @@
|
||||
"@types/micromatch": "^4.0.1",
|
||||
"@types/mime": "^3.0.4",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "20.10.6",
|
||||
"@types/node": "^22",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@uiw/react-codemirror": "^4.12.3",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
"busboy": "^1.6.0",
|
||||
"chai": "^4.3.4",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-config-preact": "^1.3.0",
|
||||
"eslint-plugin-mocha": "^10.1.0",
|
||||
"homerunner-client": "^1.0.0",
|
||||
"chai": "^4",
|
||||
"eslint": "^9.15.0",
|
||||
"eslint-plugin-chai-expect": "^3.1.0",
|
||||
"eslint-plugin-mocha": "^10.5.0",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"homerunner-client": "^1.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"mini.css": "^3.0.1",
|
||||
"mocha": "^10.2.0",
|
||||
"nyc": "^15.1.0",
|
||||
"preact": "^10.5.15",
|
||||
"rimraf": "^5.0.5",
|
||||
"sass": "^1.69.6",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.8"
|
||||
"mocha": "^10.8.2",
|
||||
"nyc": "^17.1.0",
|
||||
"preact": "^10.24.3",
|
||||
"rimraf": "6.0.1",
|
||||
"sass": "^1.81.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.16.0",
|
||||
"vite": "^5.4.11"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ export async function createHS(localparts: string[] = [], workerId: number, cryp
|
||||
URL: `http://${COMPLEMENT_HOSTNAME_RUNNING_COMPLEMENT}:${appPort}`,
|
||||
SenderLocalpart: 'hookshot',
|
||||
RateLimited: false,
|
||||
...{ASToken: asToken,
|
||||
ASToken: asToken,
|
||||
HSToken: hsToken,
|
||||
SendEphemeral: true,
|
||||
EnableEncryption: true},
|
||||
EnableEncryption: true,
|
||||
}]
|
||||
}],
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import "reflect-metadata";
|
||||
import { AdminAccountData, AdminRoomCommandHandler, Category } from "./AdminRoomCommandHandler";
|
||||
import { botCommand, compileBotCommands, handleCommand, BotCommands, HelpFunction } from "./BotCommands";
|
||||
|
@ -13,7 +13,6 @@ export enum Category {
|
||||
|
||||
|
||||
export interface AdminAccountData {
|
||||
// eslint-disable-next-line camelcase
|
||||
admin_user: string;
|
||||
github?: {
|
||||
notifications?: {
|
||||
|
@ -39,7 +39,6 @@ import { JiraOAuthRequestCloud, JiraOAuthRequestOnPrem, JiraOAuthRequestResult }
|
||||
import { GenericWebhookEvent, GenericWebhookEventResult } from "./generic/types";
|
||||
import { SetupWidget } from "./Widgets/SetupWidget";
|
||||
import { FeedEntry, FeedError, FeedReader, FeedSuccess } from "./feeds/FeedReader";
|
||||
import PQueue from "p-queue";
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { HoundConnection, HoundPayload } from "./Connections/HoundConnection";
|
||||
import { HoundReader } from "./hound/reader";
|
||||
@ -98,7 +97,7 @@ export class Bridge {
|
||||
while (!reached) {
|
||||
try {
|
||||
// Make a request to determine if we can reach the homeserver
|
||||
await this.as.botIntent.getJoinedRooms();
|
||||
await this.as.botIntent.underlyingClient.getWhoAmI();
|
||||
reached = true;
|
||||
} catch (e) {
|
||||
log.warn("Failed to connect to homeserver, retrying in 5s", e);
|
||||
@ -692,24 +691,23 @@ export class Bridge {
|
||||
(c, data) => c.handleNewActivity(data.activity)
|
||||
);
|
||||
|
||||
const queue = new PQueue({
|
||||
concurrency: 2,
|
||||
});
|
||||
// Set up already joined rooms
|
||||
await queue.addAll(this.botUsersManager.joinedRooms.map((roomId) => async () => {
|
||||
const allRooms = this.botUsersManager.joinedRooms;
|
||||
|
||||
const processRooms = async () => {
|
||||
for (let roomId = allRooms.pop(); roomId !== undefined; roomId = allRooms.pop()) {
|
||||
log.debug("Fetching state for " + roomId);
|
||||
|
||||
try {
|
||||
await connManager.createConnectionsForRoomId(roomId, false);
|
||||
} catch (ex) {
|
||||
log.error(`Unable to create connection for ${roomId}`, ex);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
const botUser = this.botUsersManager.getBotUserInRoom(roomId);
|
||||
if (!botUser) {
|
||||
log.error(`Failed to find a bot in room '${roomId}' when setting up admin room`);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Refactor this to be a connection
|
||||
@ -723,7 +721,7 @@ export class Bridge {
|
||||
);
|
||||
if (!accountData) {
|
||||
log.debug(`Room ${roomId} has no connections and is not an admin room`);
|
||||
return;
|
||||
continue;
|
||||
} else {
|
||||
// Upgrade the room
|
||||
await botUser.intent.underlyingClient.setRoomAccountData(BRIDGE_ROOM_TYPE, roomId, accountData);
|
||||
@ -752,7 +750,11 @@ export class Bridge {
|
||||
} catch (ex) {
|
||||
log.error(`Failed to set up admin room ${roomId}:`, ex);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Concurrency of two.
|
||||
const roomQueue = await Promise.all([processRooms(), processRooms()])
|
||||
|
||||
// Handle spaces
|
||||
for (const discussion of connManager.getAllConnectionsOfType(GitHubDiscussionSpace)) {
|
||||
@ -785,7 +787,7 @@ export class Bridge {
|
||||
if (this.config.metrics?.enabled) {
|
||||
this.listener.bindResource('metrics', Metrics.expressRouter);
|
||||
}
|
||||
await queue.onIdle();
|
||||
await roomQueue;
|
||||
log.info(`All connections loaded`);
|
||||
|
||||
// Load feeds after connections, to limit the chances of us double
|
||||
|
@ -16,7 +16,6 @@ const log = new Logger("CommentProcessor");
|
||||
const mime = import('mime');
|
||||
|
||||
interface IMatrixCommentEvent extends MatrixMessageContent {
|
||||
// eslint-disable-next-line camelcase
|
||||
external_url: string;
|
||||
"uk.half-shot.matrix-hookshot.github.comment": {
|
||||
id: number;
|
||||
@ -158,7 +157,7 @@ export class CommentProcessor {
|
||||
|
||||
body = body.replace(rawUrl, url);
|
||||
} catch (ex) {
|
||||
log.warn("Failed to upload file");
|
||||
log.warn("Failed to upload file", ex);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
|
@ -65,7 +65,7 @@ export class FigmaFileConnection extends BaseConnection implements IConnection {
|
||||
}
|
||||
}
|
||||
|
||||
private readonly grantChecker: GrantChecker<{fileId: string, instanceName: string}> = new ConfigGrantChecker("figma", this.as, this.config);
|
||||
private readonly grantChecker: GrantChecker<{fileId: string, instanceName: string}>;
|
||||
|
||||
constructor(
|
||||
roomId: string,
|
||||
@ -76,6 +76,7 @@ export class FigmaFileConnection extends BaseConnection implements IConnection {
|
||||
private readonly intent: Intent,
|
||||
private readonly storage: IBridgeStorageProvider) {
|
||||
super(roomId, stateKey, FigmaFileConnection.CanonicalEventType)
|
||||
this.grantChecker = new ConfigGrantChecker("figma", this.as, this.config);
|
||||
}
|
||||
|
||||
public isInterestedInStateEvent() {
|
||||
|
@ -20,7 +20,6 @@ export interface GitHubIssueConnectionState {
|
||||
repo: string;
|
||||
state: string;
|
||||
issues: string[];
|
||||
// eslint-disable-next-line camelcase
|
||||
comments_processed: number;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { ConfigGrantChecker, GrantChecker } from "../grants/GrantCheck";
|
||||
import { BridgeConfig } from "../config/Config";
|
||||
|
||||
export interface GitHubProjectConnectionState {
|
||||
// eslint-disable-next-line camelcase
|
||||
project_id: number;
|
||||
state: "open"|"closed";
|
||||
}
|
||||
|
@ -549,7 +549,7 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
|
||||
|
||||
public debounceOnIssueLabeled = new Map<number, {labels: Set<string>, timeout: NodeJS.Timeout}>();
|
||||
|
||||
private readonly grantChecker = new GitHubGrantChecker(this.as, this.tokenStore);
|
||||
private readonly grantChecker;
|
||||
|
||||
constructor(
|
||||
roomId: string,
|
||||
@ -576,6 +576,7 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
|
||||
this.hookFilter = new HookFilter(
|
||||
state.enableHooks,
|
||||
)
|
||||
this.grantChecker = new GitHubGrantChecker(this.as, this.tokenStore);
|
||||
}
|
||||
|
||||
public get hotlinkIssues() {
|
||||
@ -1463,7 +1464,7 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
|
||||
},
|
||||
name: r.name,
|
||||
description: r.description,
|
||||
avatar: r.owner?.avatar_url || r.organization?.avatar_url,
|
||||
avatar: r.owner.avatar_url,
|
||||
})) as GitHubRepoConnectionRepoTarget[];
|
||||
} catch (ex) {
|
||||
log.warn(`Failed to fetch accessible repos for ${filters.orgName} / ${userId}`, ex);
|
||||
|
@ -76,7 +76,6 @@ export class GitHubUserSpace extends BaseConnection implements IConnection {
|
||||
throw Error("Could not find repo");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
let avatarState: {type: "m.room.avatar", state_key: "", content: { url: string}}|undefined;
|
||||
try {
|
||||
if (avatarUrl) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable camelcase */
|
||||
export interface GitLabAuthor {
|
||||
id: number;
|
||||
name: string;
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
export interface IGitLabWebhookEvent {
|
||||
object_kind: string;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable camelcase */
|
||||
export interface MatrixEvent<T extends MatrixEventContent|unknown> {
|
||||
content: T;
|
||||
event_id: string;
|
||||
@ -8,10 +7,7 @@ export interface MatrixEvent<T extends MatrixEventContent|unknown> {
|
||||
type: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface MatrixEventContent {
|
||||
|
||||
}
|
||||
type MatrixEventContent = object;
|
||||
|
||||
export interface MatrixMemberContent extends MatrixEventContent {
|
||||
avatar_url: string|null;
|
||||
|
@ -48,7 +48,7 @@ export class UserNotificationWatcher {
|
||||
[...this.userIntervals.values()].forEach((v) => {
|
||||
v.stop();
|
||||
});
|
||||
this.queue.stop ? this.queue.stop() : undefined;
|
||||
this.queue.stop?.();
|
||||
}
|
||||
|
||||
public removeUser(userId: string, type: "github"|"gitlab", instanceUrl?: string) {
|
||||
|
@ -10,7 +10,6 @@ import { GitHubUserNotification } from "./github/Types";
|
||||
import { components } from "@octokit/openapi-types/types";
|
||||
import { NotifFilter } from "./NotificationFilters";
|
||||
|
||||
|
||||
const log = new Logger("NotificationProcessor");
|
||||
const md = new markdown();
|
||||
|
||||
@ -21,18 +20,15 @@ export interface IssueDiff {
|
||||
merged: boolean;
|
||||
mergedBy: null|{
|
||||
login: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
html_url: string;
|
||||
};
|
||||
user: {
|
||||
login: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
html_url: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CachedReviewData {
|
||||
// eslint-disable-next-line camelcase
|
||||
requested_reviewers: PullsListRequestedReviewersResponseData;
|
||||
reviews: PullsListReviewsResponseData;
|
||||
}
|
||||
@ -40,8 +36,6 @@ export interface CachedReviewData {
|
||||
type PROrIssue = IssuesGetResponseData|PullGetResponseData;
|
||||
|
||||
export class NotificationProcessor {
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
private static formatUser(user: {login: string, html_url: string}) {
|
||||
return `**[${user.login}](${user.html_url})**`;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { ApiError, ErrCode, Logger } from "matrix-appservice-bridge";
|
||||
import qs from "querystring";
|
||||
import axios from "axios";
|
||||
import { IGitLabWebhookEvent, IGitLabWebhookIssueStateEvent, IGitLabWebhookMREvent, IGitLabWebhookReleaseEvent } from "./Gitlab/WebhookTypes";
|
||||
import { EmitterWebhookEvent, EmitterWebhookEventName, Webhooks as OctokitWebhooks } from "@octokit/webhooks"
|
||||
import { EmitterWebhookEvent, Webhooks as OctokitWebhooks } from "@octokit/webhooks"
|
||||
import { IJiraWebhookEvent } from "./jira/WebhookTypes";
|
||||
import { JiraWebhooksRouter } from "./jira/Router";
|
||||
import { OAuthRequest } from "./WebhookTypes";
|
||||
@ -18,6 +18,7 @@ import { FigmaWebhooksRouter } from "./figma/router";
|
||||
import { GenericWebhooksRouter } from "./generic/Router";
|
||||
import { GithubInstance } from "./github/GithubInstance";
|
||||
import QuickLRU from "@alloc/quick-lru";
|
||||
import type { WebhookEventName } from "@octokit/webhooks-types";
|
||||
|
||||
const log = new Logger("Webhooks");
|
||||
|
||||
@ -178,7 +179,7 @@ export class Webhooks extends EventEmitter {
|
||||
}
|
||||
this.ghWebhooks.verifyAndReceive({
|
||||
id: githubGuid as string,
|
||||
name: req.headers["x-github-event"] as EmitterWebhookEventName,
|
||||
name: req.headers["x-github-event"] as WebhookEventName,
|
||||
payload: githubData.payload,
|
||||
signature: githubData.signature,
|
||||
}).catch((err) => {
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import YAML from "yaml";
|
||||
import { promises as fs } from "fs";
|
||||
import { IAppserviceRegistration, LogLevel, MatrixClient } from "matrix-bot-sdk";
|
||||
@ -49,11 +51,8 @@ interface BridgeConfigGitHubYAML {
|
||||
secret: string;
|
||||
};
|
||||
oauth?: {
|
||||
// eslint-disable-next-line camelcase
|
||||
client_id: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
client_secret: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
redirect_uri: string;
|
||||
};
|
||||
defaultOptions?: GitHubRepoConnectionOptions;
|
||||
@ -72,11 +71,8 @@ export class BridgeConfigGitHub {
|
||||
};
|
||||
@configKey("Settings for allowing users to sign in via OAuth.", true)
|
||||
readonly oauth?: {
|
||||
// eslint-disable-next-line camelcase
|
||||
client_id: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
client_secret: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
redirect_uri: string;
|
||||
};
|
||||
@configKey("Default options for GitHub connections.", true)
|
||||
@ -109,18 +105,14 @@ export class BridgeConfigGitHub {
|
||||
}
|
||||
|
||||
export interface BridgeConfigJiraCloudOAuth {
|
||||
// eslint-disable-next-line camelcase
|
||||
client_id: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
client_secret: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
redirect_uri: string;
|
||||
}
|
||||
|
||||
export interface BridgeConfigJiraOnPremOAuth {
|
||||
consumerKey: string;
|
||||
privateKey: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
redirect_uri: string;
|
||||
}
|
||||
|
||||
@ -184,11 +176,6 @@ export class BridgeConfigJira implements BridgeConfigJiraYAML {
|
||||
|
||||
export interface GitLabInstance {
|
||||
url: string;
|
||||
// oauth: {
|
||||
// client_id: string;
|
||||
// client_secret: string;
|
||||
// redirect_uri: string;
|
||||
// };
|
||||
}
|
||||
|
||||
export interface BridgeConfigGitLabYAML {
|
||||
@ -744,11 +731,9 @@ export async function parseRegistrationFile(filename: string) {
|
||||
if (require.main === module) {
|
||||
Logger.configure({console: "info"});
|
||||
BridgeConfig.parseConfig(process.argv[2] || "config.yml", process.env).then(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Config successfully validated.');
|
||||
process.exit(0);
|
||||
}).catch(ex => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error in config:', ex);
|
||||
process.exit(1);
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { BridgeConfig, BridgeConfigRoot } from "./Config";
|
||||
import { getConfigKeyMetadata, keyIsHidden } from "./Decorators";
|
||||
import { Node, YAMLSeq, default as YAML } from "yaml";
|
||||
@ -246,19 +248,16 @@ async function renderRegistrationFile(configPath?: string) {
|
||||
rooms: [],
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
console.log(YAML.stringify(obj));
|
||||
}
|
||||
|
||||
|
||||
// Can be called directly
|
||||
if (require.main === module) {
|
||||
if (process.argv[2] === '--config') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(renderDefaultConfig());
|
||||
} else if (process.argv[2] === '--registration') {
|
||||
renderRegistrationFile(process.argv[3]).catch(ex => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(ex);
|
||||
process.exit(1);
|
||||
});
|
||||
|
@ -5,10 +5,8 @@ export interface GenericWebhookEvent {
|
||||
hookId: string;
|
||||
}
|
||||
|
||||
|
||||
export type GenericWebhookEventResult = GenericWebhookEventResultSuccess | GenericWebhookEventResultFailure;
|
||||
|
||||
|
||||
export interface GenericWebhookEventResultSuccess {
|
||||
successful: true|null;
|
||||
response?: WebhookResponse,
|
||||
|
@ -46,14 +46,14 @@ interface OAuthUrlParameters {
|
||||
export class GithubInstance {
|
||||
private internalOctokit!: Octokit;
|
||||
private readonly installationsCache = new Map<number, Installation>();
|
||||
private internalAppSlug?: string;
|
||||
private internalAppUrl?: string;
|
||||
|
||||
constructor (private readonly appId: number|string, private readonly privateKey: string, private readonly baseUrl: URL) {
|
||||
this.appId = parseInt(appId as string, 10);
|
||||
}
|
||||
|
||||
public get appSlug() {
|
||||
return this.internalAppSlug;
|
||||
public get appUrl() {
|
||||
return this.internalAppUrl;
|
||||
}
|
||||
|
||||
public get appOctokit() {
|
||||
@ -143,7 +143,10 @@ export class GithubInstance {
|
||||
});
|
||||
|
||||
const appDetails = await this.internalOctokit.apps.getAuthenticated();
|
||||
this.internalAppSlug = appDetails.data.slug;
|
||||
if (!appDetails.data) {
|
||||
throw Error("No information returned about GitHub App. Is your GitHub App configured correctly?");
|
||||
}
|
||||
this.internalAppUrl = appDetails.data.html_url;
|
||||
|
||||
let installPageSize = 100;
|
||||
let page = 1;
|
||||
@ -186,12 +189,10 @@ export class GithubInstance {
|
||||
}
|
||||
|
||||
public get newInstallationUrl() {
|
||||
if (this.baseUrl.hostname === GITHUB_CLOUD_URL.hostname) {
|
||||
// Cloud
|
||||
return new URL(`/apps/${this.appSlug}/installations/new`, GITHUB_CLOUD_PUBLIC_URL);
|
||||
if (!this.appUrl) {
|
||||
throw Error('No configured app url, cannot get installation url');
|
||||
}
|
||||
// Enterprise (yes, i know right)
|
||||
return new URL(`/github-apps/${this.appSlug}/installations/new`, this.baseUrl);
|
||||
return new URL(this.appUrl);
|
||||
}
|
||||
|
||||
public static generateOAuthUrl(baseUrl: URL, action: "authorize"|"access_token", params: OAuthUrlParameters) {
|
||||
|
@ -19,7 +19,6 @@ export type CreateInstallationAccessTokenDataType = Endpoints["POST /app/install
|
||||
|
||||
export const NAMELESS_ORG_PLACEHOLDER = "No name";
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
export interface GitHubUserNotification {
|
||||
id: string;
|
||||
reason: "assign"|"author"|"comment"|"invitation"|"manual"|"mention"|"review_requested"|
|
||||
|
@ -26,7 +26,7 @@ export class JiraGrantChecker extends GrantChecker<JiraGrantConnectionId> {
|
||||
try {
|
||||
await JiraProjectConnection.assertUserHasAccessToProject(this.tokenStore, sender, connectionId.url);
|
||||
return true;
|
||||
} catch (ex) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,6 @@ const ACCESSIBLE_RESOURCE_CACHE_TTL_MS = 60000;
|
||||
|
||||
|
||||
export class HookshotCloudJiraApi extends HookshotJiraApi {
|
||||
constructor(options: JiraApi.JiraApiOptions, res: JiraAPIAccessibleResource) {
|
||||
super(options, res);
|
||||
}
|
||||
|
||||
|
||||
async getIssue(issueIdOrKey: string): Promise<JiraIssue> {
|
||||
return this.apiRequest<JiraIssue>(`/rest/api/3/issue/${issueIdOrKey}`);
|
||||
}
|
||||
@ -90,7 +85,7 @@ export class JiraCloudClient implements JiraClient {
|
||||
if (existingPromise) {
|
||||
return await existingPromise;
|
||||
}
|
||||
} catch (ex) {
|
||||
} catch {
|
||||
// Existing failed promise, break out and try again.
|
||||
JiraCloudClient.resourceCache.delete(this.bearer);
|
||||
}
|
||||
|
@ -2,6 +2,11 @@ try {
|
||||
// In production, we expect it co-located
|
||||
module.exports = require('./matrix-hookshot-rs.node');
|
||||
} catch (ex) {
|
||||
try {
|
||||
// When running under ts-node, it may not be co-located.
|
||||
module.exports = require('../lib/matrix-hookshot-rs.node');
|
||||
} catch (ex) {
|
||||
// Or in a test environment.
|
||||
module.exports = require('../../lib/matrix-hookshot-rs.node');
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ async function constructFeedReader(feedResponse: () => {headers: Record<string,s
|
||||
const feedReader = new FeedReader(
|
||||
config, cm, mq, storage,
|
||||
);
|
||||
// eslint-disable-next-line mocha/no-top-level-hooks
|
||||
after(() => httpServer.close());
|
||||
return {config, cm, events, feedReader, feedUrl, httpServer, storage};
|
||||
}
|
||||
@ -100,6 +101,7 @@ describe("FeedReader", () => {
|
||||
expect(events[0].data.feed.title).to.equal(null);
|
||||
expect(events[0].data.title).to.equal(null);
|
||||
});
|
||||
|
||||
it("should handle RSS 2.0 feeds", async () => {
|
||||
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
|
||||
headers: {}, data: `
|
||||
@ -137,6 +139,7 @@ describe("FeedReader", () => {
|
||||
expect(events[0].data.link).to.equal('http://www.example.com/blog/post/1');
|
||||
expect(events[0].data.pubdate).to.equal('Sun, 6 Sep 2009 16:20:00 +0000');
|
||||
});
|
||||
|
||||
it("should handle RSS feeds with a permalink url", async () => {
|
||||
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
|
||||
headers: {}, data: `
|
||||
@ -173,6 +176,7 @@ describe("FeedReader", () => {
|
||||
expect(events[0].data.link).to.equal('http://www.example.com/blog/post/1');
|
||||
expect(events[0].data.pubdate).to.equal('Sun, 6 Sep 2009 16:20:00 +0000');
|
||||
});
|
||||
|
||||
it("should handle Atom feeds", async () => {
|
||||
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
|
||||
headers: {}, data: `
|
||||
@ -213,6 +217,7 @@ describe("FeedReader", () => {
|
||||
expect(events[0].data.link).to.equal('http://example.org/2003/12/13/atom03');
|
||||
expect(events[0].data.pubdate).to.equal('Sat, 13 Dec 2003 18:30:02 +0000');
|
||||
});
|
||||
|
||||
it("should not duplicate feed entries", async () => {
|
||||
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
|
||||
headers: {}, data: `
|
||||
@ -238,6 +243,7 @@ describe("FeedReader", () => {
|
||||
feedReader.stop();
|
||||
expect(events).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it("should always hash to the same value for Atom feeds", async () => {
|
||||
const expectedHash = ['md5:d41d8cd98f00b204e9800998ecf8427e'];
|
||||
const { feedReader, feedUrl, storage } = await constructFeedReader(() => ({
|
||||
@ -258,6 +264,7 @@ describe("FeedReader", () => {
|
||||
const items = await storage.hasSeenFeedGuids(feedUrl, ...expectedHash);
|
||||
expect(items).to.deep.equal(expectedHash);
|
||||
});
|
||||
|
||||
it("should always hash to the same value for RSS feeds", async () => {
|
||||
const expectedHash = [
|
||||
'md5:98bafde155b931e656ad7c137cd7711e', // guid
|
||||
|
@ -48,11 +48,13 @@ describe("FormatUtilTest", () => {
|
||||
"evilcorp/lab: A simple description",
|
||||
);
|
||||
});
|
||||
|
||||
it("should correctly formats a issue room name", () => {
|
||||
expect(FormatUtil.formatIssueRoomName(SIMPLE_ISSUE, SIMPLE_REPO)).to.equal(
|
||||
"evilcorp/lab#123: A simple title",
|
||||
);
|
||||
});
|
||||
|
||||
it("should correctly generate a partial body for a Github repo", () => {
|
||||
expect(FormatUtil.getPartialBodyForGithubRepo(SIMPLE_REPO)).to.deep.equal({
|
||||
"external_url": "https://github.com/evilcorp/lab",
|
||||
@ -63,6 +65,7 @@ describe("FormatUtilTest", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly generate a partial body for a Github issue", () => {
|
||||
expect(FormatUtil.getPartialBodyForGithubIssue(SIMPLE_REPO, SIMPLE_ISSUE)).to.deep.equal({
|
||||
"external_url": "https://github.com/evilcorp/lab/issues/123",
|
||||
@ -79,29 +82,34 @@ describe("FormatUtilTest", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly formats a room topic", () => {
|
||||
expect(FormatUtil.formatRoomTopic(SIMPLE_ISSUE)).to.equal(
|
||||
"Status: open | https://github.com/evilcorp/lab/issues/123",
|
||||
);
|
||||
});
|
||||
|
||||
it("should correctly format one simple label", () => {
|
||||
expect(FormatUtil.formatLabels([{name: "foo"}])).to.deep.equal({
|
||||
plain: "foo",
|
||||
html: "<span>foo</span>"
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly format many simple labels", () => {
|
||||
expect(FormatUtil.formatLabels([{name: "foo"},{name: "bar"}])).to.deep.equal({
|
||||
plain: "foo, bar",
|
||||
html: "<span>foo</span> <span>bar</span>"
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly format one detailed label", () => {
|
||||
expect(FormatUtil.formatLabels([{name: "foo", color: 'FFFFFF', description: 'My label'}])).to.deep.equal({
|
||||
plain: "foo",
|
||||
html: "<span data-mx-bg-color=\"#FFFFFF\" data-mx-color=\"#000000\" title=\"My label\">foo</span>"
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly format many detailed labels", () => {
|
||||
expect(FormatUtil.formatLabels([
|
||||
{name: "foo", color: 'FFFFFF', description: 'My label'},
|
||||
@ -112,6 +120,7 @@ describe("FormatUtilTest", () => {
|
||||
+ "<span data-mx-bg-color=\"#AACCEE\" data-mx-color=\"#000000\" title=\"My other label\">bar</span>"
|
||||
},);
|
||||
});
|
||||
|
||||
it("should correctly format a JIRA issue", () => {
|
||||
expect(FormatUtil.getPartialBodyForJiraIssue(SIMPLE_JIRA_ISSUE)).to.deep.equal({
|
||||
"external_url": "http://example-api.url.com/browse/TEST-001",
|
||||
@ -127,6 +136,7 @@ describe("FormatUtilTest", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should hash an ID", () => {
|
||||
expect(FormatUtil.hashId("foobar")).to.equal('3858f62230ac3c915f300c664312c63f');
|
||||
});
|
||||
|
@ -6,13 +6,16 @@ const ENABLED_SET = ['enabled-hook', 'enabled-but-ignored'];
|
||||
|
||||
describe("HookFilter", () => {
|
||||
let filter: HookFilter<string>;
|
||||
|
||||
beforeEach(() => {
|
||||
filter = new HookFilter(ENABLED_SET);
|
||||
});
|
||||
|
||||
describe('shouldSkip', () => {
|
||||
it('should allow a hook named in enabled set', () => {
|
||||
expect(filter.shouldSkip('enabled-hook')).to.be.false;
|
||||
});
|
||||
|
||||
it('should not allow a hook not named in enabled set', () => {
|
||||
expect(filter.shouldSkip('not-enabled-hook')).to.be.true;
|
||||
});
|
||||
|
@ -40,7 +40,7 @@ describe("IntentUtils", () => {
|
||||
expect(hasInvited).to.be.true;
|
||||
});
|
||||
|
||||
it("invites the user to the room and joins", () => {
|
||||
it("invites the user to the room and handles the failure", () => {
|
||||
const targetIntent = IntentMock.create(SENDER_USER_ID);
|
||||
const matrixClient = MatrixClientMock.create();
|
||||
|
||||
|
@ -23,6 +23,7 @@ describe("MessageQueueTest", () => {
|
||||
data: 51,
|
||||
});
|
||||
});
|
||||
|
||||
it("should be able to push an event, and respond to it", async () => {
|
||||
mq.subscribe("fakeevent2");
|
||||
mq.subscribe("response.fakeevent2");
|
||||
|
@ -12,6 +12,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.queue).to.be.undefined;
|
||||
expect(config.cache?.redisUri).to.equal("redis://localhost:6379");
|
||||
});
|
||||
|
||||
it("with a host parameter", () => {
|
||||
const config = new BridgeConfig({ ...DefaultConfigRoot, queue: {
|
||||
monolithic: true,
|
||||
@ -20,6 +21,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.queue).to.be.undefined;
|
||||
expect(config.cache?.redisUri).to.equal("redis://bark:6379");
|
||||
});
|
||||
|
||||
it("with a port parameter", () => {
|
||||
const config = new BridgeConfig({ ...DefaultConfigRoot, queue: {
|
||||
monolithic: true,
|
||||
@ -28,6 +30,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.queue).to.be.undefined;
|
||||
expect(config.cache?.redisUri).to.equal("redis://localhost:6379");
|
||||
});
|
||||
|
||||
it("with a host and port parameter", () => {
|
||||
const config = new BridgeConfig({ ...DefaultConfigRoot, queue: {
|
||||
monolithic: true,
|
||||
@ -37,6 +40,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.queue).to.be.undefined;
|
||||
expect(config.cache?.redisUri).to.equal("redis://bark:6379");
|
||||
});
|
||||
|
||||
it("with monolithic disabled", () => {
|
||||
const config = new BridgeConfig({
|
||||
...DefaultConfigRoot,
|
||||
@ -51,6 +55,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.cache?.redisUri).to.equal("redis://localhost:6379");
|
||||
});
|
||||
});
|
||||
|
||||
describe("will handle the queue option", () => {
|
||||
it("with redisUri", () => {
|
||||
const config = new BridgeConfig({ ...DefaultConfigRoot,
|
||||
@ -66,6 +71,7 @@ describe("Config/BridgeConfig", () => {
|
||||
expect(config.cache).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe("will handle the cache option", () => {
|
||||
it("with redisUri", () => {
|
||||
const config = new BridgeConfig({
|
||||
|
@ -21,43 +21,53 @@ describe("Config/BridgePermissions", () => {
|
||||
const bridgePermissions = new BridgePermissions([]);
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "empty-service", "commands")).to.be.false;
|
||||
});
|
||||
|
||||
it("will return false for an insufficent level", () => {
|
||||
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "notifications")).to.be.false;
|
||||
});
|
||||
|
||||
it("will return false if the there are no matching services", () => {
|
||||
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "other-service", "login")).to.be.false;
|
||||
});
|
||||
|
||||
it("will return false if the target does not match", () => {
|
||||
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:baz", "my-service", "login")).to.be.false;
|
||||
});
|
||||
|
||||
it("will return true if there is a matching level and service", () => {
|
||||
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
|
||||
});
|
||||
|
||||
it("will return true for a matching actor domain", () => {
|
||||
const bridgePermissions = genBridgePermissions('bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
|
||||
});
|
||||
|
||||
it("will return true for a wildcard actor", () => {
|
||||
const bridgePermissions = genBridgePermissions('*', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
|
||||
});
|
||||
|
||||
it("will return true for a wildcard service", () => {
|
||||
const bridgePermissions = genBridgePermissions('@foo:bar', '*', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
|
||||
});
|
||||
|
||||
it("will return false if a user is not present in a room", () => {
|
||||
const bridgePermissions = genBridgePermissions('!foo:bar', 'my-service', 'login');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.false;
|
||||
});
|
||||
|
||||
it("will return true if a user is present in a room", () => {
|
||||
const bridgePermissions = genBridgePermissions('!foo:bar', 'my-service', 'login');
|
||||
bridgePermissions.addMemberToCache('!foo:bar', '@foo:bar');
|
||||
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
|
||||
});
|
||||
|
||||
it("will fall through and return true for multiple permission sets", () => {
|
||||
const bridgePermissions = new BridgePermissions([
|
||||
{
|
||||
@ -98,6 +108,7 @@ describe("Config/BridgePermissions", () => {
|
||||
const bridgePermissions = new BridgePermissions([]);
|
||||
expect(bridgePermissions.checkActionAny("@foo:bar", "commands")).to.be.false;
|
||||
});
|
||||
|
||||
it(`will return false for a service with an insufficent level`, () => {
|
||||
const bridgePermissions = genBridgePermissions("@foo:bar", "fake-service", "commands");
|
||||
expect(
|
||||
@ -107,8 +118,7 @@ describe("Config/BridgePermissions", () => {
|
||||
)
|
||||
).to.be.false;
|
||||
});
|
||||
const checkActorValues = ["@foo:bar", "bar", "*"];
|
||||
checkActorValues.forEach(actor => {
|
||||
for (const actor of ["@foo:bar", "bar", "*"]) {
|
||||
it(`will return true for a service defintion of '${actor}' that has a sufficent level`, () => {
|
||||
const bridgePermissions = genBridgePermissions("@foo:bar", "fake-service", "commands");
|
||||
expect(
|
||||
@ -118,6 +128,6 @@ describe("Config/BridgePermissions", () => {
|
||||
)
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { AppserviceMock } from "../utils/AppserviceMock";
|
||||
import { FeedConnection, FeedConnectionState } from "../../src/Connections";
|
||||
import { FeedEntry } from "../../src/feeds/FeedReader";
|
||||
@ -47,6 +46,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.content.external_url).to.equal(FEED_ENTRY_DEFAULTS.link);
|
||||
expect(matrixEvt.content.body).to.equal("New post in Test feed: [Foo](foo/bar)");
|
||||
});
|
||||
|
||||
it("will handle simple feed message without a title and link ", async () => {
|
||||
const [connection, intent] = createFeed();
|
||||
await connection.handleFeedEntry({
|
||||
@ -60,6 +60,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.content.external_url).to.be.undefined;
|
||||
expect(matrixEvt.content.body).to.equal("New post in Test feed");
|
||||
});
|
||||
|
||||
it("will handle simple feed message with a missing title ", async () => {
|
||||
const [connection, intent] = createFeed();
|
||||
await connection.handleFeedEntry({
|
||||
@ -71,6 +72,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.roomId).to.equal(ROOM_ID);
|
||||
expect(matrixEvt.content.body).to.equal("New post in Test feed: [foo/bar](foo/bar)");
|
||||
});
|
||||
|
||||
it("will handle simple feed message with a missing link ", async () => {
|
||||
const [connection, intent] = createFeed();
|
||||
await connection.handleFeedEntry({
|
||||
@ -82,6 +84,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.roomId).to.equal(ROOM_ID);
|
||||
expect(matrixEvt.content.body).to.equal("New post in Test feed: Foo");
|
||||
});
|
||||
|
||||
it("will handle simple feed message with all the template options possible ", async () => {
|
||||
const [connection, intent] = createFeed({
|
||||
template: `$FEEDNAME $FEEDURL $FEEDTITLE $TITLE $LINK $AUTHOR $DATE $SUMMARY`
|
||||
@ -94,6 +97,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.roomId).to.equal(ROOM_ID);
|
||||
expect(matrixEvt.content.body).to.equal("Test feed https://example.com/feed.xml Test feed Foo [Foo](foo/bar) Me! today! fibble fobble");
|
||||
});
|
||||
|
||||
it("will handle html in the feed summary ", async () => {
|
||||
const [connection, intent] = createFeed({
|
||||
template: `$FEEDNAME $SUMMARY`
|
||||
@ -107,6 +111,7 @@ describe("FeedConnection", () => {
|
||||
expect(matrixEvt.roomId).to.equal(ROOM_ID);
|
||||
expect(matrixEvt.content.body).to.equal('Test feed <p> Some HTML with which should be ignored and an <img src="mxc://fibble/fobble"> </p>');
|
||||
});
|
||||
|
||||
it("will handle partial html in the feed summary ", async () => {
|
||||
const [connection, intent] = createFeed({
|
||||
template: `$FEEDNAME $SUMMARY`
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { assert, expect } from "chai";
|
||||
import { Appservice, Intent, MatrixError } from "matrix-bot-sdk";
|
||||
import { BridgeConfigGenericWebhooks, BridgeGenericWebhooksConfigYAML } from "../../src/config/sections";
|
||||
@ -75,10 +74,12 @@ describe("GenericHookConnection", () => {
|
||||
before(async () => {
|
||||
await GenericHookConnection.initialiseQuickJS();
|
||||
})
|
||||
|
||||
it("will handle simple hook events", async () => {
|
||||
const [connection, mq] = createGenericHook();
|
||||
await testSimpleWebhook(connection, mq, "data");
|
||||
});
|
||||
|
||||
it("will handle a hook event containing text", async () => {
|
||||
const webhookData = {text: "simple-message"};
|
||||
const [connection, mq] = createGenericHook();
|
||||
@ -97,6 +98,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event containing markdown", async () => {
|
||||
const webhookData = {text: "**bold-message** _italic-message_"};
|
||||
const [connection, mq] = createGenericHook();
|
||||
@ -115,6 +117,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event containing markdown with newlines", async () => {
|
||||
const webhookData = {text: "# Oh wow\n\n`some-code`"};
|
||||
const [connection, mq] = createGenericHook();
|
||||
@ -133,6 +136,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event containing html", async () => {
|
||||
const webhookData = {text: "simple-message", html: "<b>simple-message</b>"};
|
||||
const [connection, mq] = createGenericHook();
|
||||
@ -151,6 +155,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event containing a username", async () => {
|
||||
const webhookData = {username: "Bobs-integration", type: 42};
|
||||
const [connection, mq] = createGenericHook();
|
||||
@ -169,6 +174,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event with a v1 transformation function", async () => {
|
||||
const webhookData = {question: 'What is the meaning of life?', answer: 42};
|
||||
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V1TFFunction}, {
|
||||
@ -190,6 +196,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event with a v2 transformation function", async () => {
|
||||
const webhookData = {question: 'What is the meaning of life?', answer: 42};
|
||||
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V2TFFunction}, {
|
||||
@ -211,6 +218,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a hook event with a top-level return", async () => {
|
||||
const webhookData = {question: 'What is the meaning of life?', answer: 42};
|
||||
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V2TFFunctionWithReturn}, {
|
||||
@ -232,6 +240,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will fail to handle a webhook with an invalid script", async () => {
|
||||
const webhookData = {question: 'What is the meaning of life?', answer: 42};
|
||||
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: "bibble bobble"}, {
|
||||
@ -253,6 +262,7 @@ describe("GenericHookConnection", () => {
|
||||
type: 'm.room.message',
|
||||
});
|
||||
});
|
||||
|
||||
it("will handle a message containing floats", async () => {
|
||||
const [connection, mq] = createGenericHook();
|
||||
let messagePromise = handleMessage(mq);
|
||||
@ -357,6 +367,7 @@ describe("GenericHookConnection", () => {
|
||||
})).to.throw("'expirationDate' must be a valid date");
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail to create a hook with a too short expiry time', async () => {
|
||||
const as = AppserviceMock.create();
|
||||
try {
|
||||
@ -375,6 +386,7 @@ describe("GenericHookConnection", () => {
|
||||
expect(ex.message).to.contain('Expiration date must at least be a hour in the future');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail to create a hook with a too long expiry time', async () => {
|
||||
const as = AppserviceMock.create();
|
||||
try {
|
||||
@ -396,6 +408,7 @@ describe("GenericHookConnection", () => {
|
||||
expect(ex.message).to.contain('Expiration date cannot exceed the configured max expiry time');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail to create a hook without an expiry time when required by config', async () => {
|
||||
const as = AppserviceMock.create();
|
||||
try {
|
||||
@ -417,12 +430,14 @@ describe("GenericHookConnection", () => {
|
||||
expect(ex.message).to.contain('Expiration date must be set');
|
||||
}
|
||||
});
|
||||
|
||||
it('should create a hook and handle a request within the expiry time', async () => {
|
||||
const [connection, mq] = createGenericHook({
|
||||
expirationDate: add(new Date(), { seconds: 30 }).toISOString(),
|
||||
});
|
||||
await testSimpleWebhook(connection, mq, "test");
|
||||
});
|
||||
|
||||
it('should reject requests to an expired hook', async () => {
|
||||
const [connection] = createGenericHook({
|
||||
expirationDate: new Date().toISOString(),
|
||||
|
@ -55,7 +55,6 @@ function createConnection(state: Record<string, unknown> = {}, isExistingState=f
|
||||
"state_key",
|
||||
githubInstance,
|
||||
// Default config always contains GitHub
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
DefaultConfig.github!
|
||||
);
|
||||
return {connection, intent: intent as IntentMock};
|
||||
@ -84,6 +83,7 @@ describe("GitHubRepoConnection", () => {
|
||||
}
|
||||
} as GitHubRepoConnectionState as unknown as Record<string, unknown>);
|
||||
});
|
||||
|
||||
it("will convert ignoredHooks for existing state", () => {
|
||||
const state = GitHubRepoConnection.validateState({
|
||||
org: "foo",
|
||||
@ -94,6 +94,7 @@ describe("GitHubRepoConnection", () => {
|
||||
} as GitHubRepoConnectionState as unknown as Record<string, unknown>, true);
|
||||
expect(state.enableHooks).to.not.contain('issue');
|
||||
});
|
||||
|
||||
it("will disallow invalid state", () => {
|
||||
try {
|
||||
GitHubRepoConnection.validateState({
|
||||
@ -106,6 +107,7 @@ describe("GitHubRepoConnection", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("will disallow enabledHooks to contains invalid enums if this is new state", () => {
|
||||
try {
|
||||
GitHubRepoConnection.validateState({
|
||||
@ -119,6 +121,7 @@ describe("GitHubRepoConnection", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("will allow enabledHooks to contains invalid enums if this is old state", () => {
|
||||
GitHubRepoConnection.validateState({
|
||||
org: "foo",
|
||||
@ -127,6 +130,7 @@ describe("GitHubRepoConnection", () => {
|
||||
}, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onIssueCreated", () => {
|
||||
it("will handle a simple issue", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
@ -136,6 +140,7 @@ describe("GitHubRepoConnection", () => {
|
||||
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.html_url, 0);
|
||||
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.title, 0);
|
||||
});
|
||||
|
||||
it("will handle assignees on issue creation", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
await connection.onIssueCreated({
|
||||
@ -151,6 +156,7 @@ describe("GitHubRepoConnection", () => {
|
||||
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.html_url, 0);
|
||||
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.title, 0);
|
||||
});
|
||||
|
||||
it("will filter out issues not matching includingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
includingLabels: ["include-me"]
|
||||
@ -168,6 +174,7 @@ describe("GitHubRepoConnection", () => {
|
||||
await connection.onIssueCreated(GITHUB_ISSUE_CREATED_PAYLOAD as never);
|
||||
intent.expectNoEvent();
|
||||
});
|
||||
|
||||
it("will filter out issues matching excludingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
excludingLabels: ["exclude-me"]
|
||||
@ -183,6 +190,7 @@ describe("GitHubRepoConnection", () => {
|
||||
} as never);
|
||||
intent.expectNoEvent();
|
||||
});
|
||||
|
||||
it("will include issues matching includingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
includingIssues: ["include-me"]
|
||||
|
@ -115,6 +115,7 @@ describe("GitLabRepoConnection", () => {
|
||||
excludingLabels: ["but-not-me"],
|
||||
} as GitLabRepoConnectionState as unknown as Record<string, unknown>);
|
||||
});
|
||||
|
||||
it("will convert ignoredHooks for existing state", () => {
|
||||
const state = GitLabRepoConnection.validateState({
|
||||
instance: "foo",
|
||||
@ -126,6 +127,7 @@ describe("GitLabRepoConnection", () => {
|
||||
} as GitLabRepoConnectionState as unknown as Record<string, unknown>, true);
|
||||
expect(state.enableHooks).to.not.contain('merge_request');
|
||||
});
|
||||
|
||||
it("will disallow invalid state", () => {
|
||||
try {
|
||||
GitLabRepoConnection.validateState({
|
||||
@ -138,6 +140,7 @@ describe("GitLabRepoConnection", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("will disallow enabledHooks to contains invalid enums if this is new state", () => {
|
||||
try {
|
||||
GitLabRepoConnection.validateState({
|
||||
@ -151,6 +154,7 @@ describe("GitLabRepoConnection", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("will allow enabledHooks to contains invalid enums if this is old state", () => {
|
||||
GitLabRepoConnection.validateState({
|
||||
instance: "bar",
|
||||
@ -159,6 +163,7 @@ describe("GitLabRepoConnection", () => {
|
||||
}, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onCommentCreated", () => {
|
||||
it("will handle an MR comment", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
@ -169,6 +174,7 @@ describe("GitLabRepoConnection", () => {
|
||||
'event body indicates MR comment'
|
||||
);
|
||||
});
|
||||
|
||||
it("will debounce MR comments", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
await connection.onCommentCreated(GITLAB_MR_COMMENT as never);
|
||||
@ -188,6 +194,7 @@ describe("GitLabRepoConnection", () => {
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
it("will add new comments in a Matrix thread", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
await connection.onCommentCreated(GITLAB_MR_COMMENT as never);
|
||||
@ -201,6 +208,7 @@ describe("GitLabRepoConnection", () => {
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("will correctly map new comments to aggregated discussions", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
await connection.onCommentCreated({
|
||||
@ -251,6 +259,7 @@ describe("GitLabRepoConnection", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onIssueCreated", () => {
|
||||
it("will handle a simple issue", async () => {
|
||||
const { connection, intent } = createConnection();
|
||||
@ -260,6 +269,7 @@ describe("GitLabRepoConnection", () => {
|
||||
intent.expectEventBodyContains(GITLAB_ISSUE_CREATED_PAYLOAD.object_attributes.url, 0);
|
||||
intent.expectEventBodyContains(GITLAB_ISSUE_CREATED_PAYLOAD.object_attributes.title, 0);
|
||||
});
|
||||
|
||||
it("will filter out issues not matching includingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
includingLabels: ["include-me"]
|
||||
@ -274,6 +284,7 @@ describe("GitLabRepoConnection", () => {
|
||||
await connection.onMergeRequestOpened(GITLAB_ISSUE_CREATED_PAYLOAD as never);
|
||||
intent.expectNoEvent();
|
||||
});
|
||||
|
||||
it("will filter out issues matching excludingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
excludingLabels: ["exclude-me"]
|
||||
@ -286,6 +297,7 @@ describe("GitLabRepoConnection", () => {
|
||||
} as never);
|
||||
intent.expectNoEvent();
|
||||
});
|
||||
|
||||
it("will include issues matching includingLabels.", async () => {
|
||||
const { connection, intent } = createConnection({
|
||||
includingIssues: ["include-me"]
|
||||
|
@ -13,6 +13,7 @@ describe("GitHub", () => {
|
||||
})
|
||||
).equals('https://github.com/login/oauth/authorize?state=my_state&client_id=123&redirect_uri=456');
|
||||
});
|
||||
|
||||
it("can generate an authorize URL for enterprise URLs", () => {
|
||||
expect(
|
||||
GithubInstance.generateOAuthUrl(new URL("https://mygithuburl.com/foo/bar"), "authorize", {
|
||||
@ -22,6 +23,7 @@ describe("GitHub", () => {
|
||||
})
|
||||
).equals('https://mygithuburl.com/foo/bar/login/oauth/authorize?state=my_state&client_id=123&redirect_uri=456');
|
||||
});
|
||||
|
||||
it("can generate an access_token URL for the cloud URL", () => {
|
||||
expect(
|
||||
GithubInstance.generateOAuthUrl(GITHUB_CLOUD_URL, "access_token", {
|
||||
@ -33,6 +35,7 @@ describe("GitHub", () => {
|
||||
})
|
||||
).equals('https://github.com/login/oauth/access_token?client_id=123&client_secret=the-secret&code=the-code&redirect_uri=456&state=my_state');
|
||||
});
|
||||
|
||||
it("can generate an access_token URL for enterprise URLs", () => {
|
||||
expect(
|
||||
GithubInstance.generateOAuthUrl(new URL("https://mygithuburl.com/foo/bar"), "access_token", {
|
||||
|
@ -39,6 +39,7 @@ describe("GrantChecker", () => {
|
||||
let check: GrantChecker<string>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let intent: any;
|
||||
|
||||
beforeEach(() => {
|
||||
intent = IntentMock.create('@foo:bar');
|
||||
check = new TestGrantChecker(intent, GRANT_SERVICE);
|
||||
@ -95,9 +96,11 @@ describe("GrantChecker", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config fallback', () => {
|
||||
let check: GrantChecker<string>;
|
||||
let as: AppserviceMock;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockAs = AppserviceMock.create();
|
||||
as = mockAs;
|
||||
|
@ -9,13 +9,15 @@ describe("Jira", () => {
|
||||
key: "TEST-111",
|
||||
})).to.equal("https://my-test-jira/browse/TEST-111");
|
||||
});
|
||||
|
||||
it("processes a jira issue into a URL with a port", () => {
|
||||
expect(generateJiraWebLinkFromIssue({
|
||||
self: "https://my-test-jira:9995/",
|
||||
key: "TEST-111",
|
||||
})).to.equal("https://my-test-jira:9995/browse/TEST-111");
|
||||
});
|
||||
it("processes a jira issue into a URL with a port", () => {
|
||||
|
||||
it("processes a jira issue into a URL with a port and a version", () => {
|
||||
expect(generateJiraWebLinkFromVersion({
|
||||
self: "https://my-test-jira:9995/",
|
||||
description: "foo",
|
||||
|
@ -53,17 +53,20 @@ describe("TokenEncryption", () => {
|
||||
}));
|
||||
|
||||
}, );
|
||||
|
||||
it('should be able to encrypt a string into a single part', async() => {
|
||||
const tokenEncryption = await createTokenEncryption();
|
||||
const result = tokenEncryption.encrypt('hello world');
|
||||
expect(result).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('should be able to decrypt from a single part into a string', async() => {
|
||||
const tokenEncryption = await createTokenEncryption();
|
||||
const value = tokenEncryption.encrypt('hello world');
|
||||
const result = tokenEncryption.decrypt(value, Algo.RSAPKCS1v15);
|
||||
expect(result).to.equal('hello world');
|
||||
});
|
||||
|
||||
it('should be able to decrypt from many parts into string', async() => {
|
||||
const plaintext = 'This is a very long string that needs to be encoded into multiple parts in order for us to store it properly. This ' +
|
||||
' should end up as multiple encrypted values in base64.';
|
||||
@ -73,11 +76,13 @@ describe("TokenEncryption", () => {
|
||||
const result = tokenEncryption.decrypt(value, Algo.RSAPKCS1v15);
|
||||
expect(result).to.equal(plaintext);
|
||||
});
|
||||
|
||||
it('should support pkcs1 format keys', async() => {
|
||||
const tokenEncryption = new TokenEncryption(await keyPromisePKCS1);
|
||||
const result = tokenEncryption.encrypt('hello world');
|
||||
expect(result).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('should be to decrypt a string from the old crypto implementation', async() => {
|
||||
const legacyString = await legacyEncryptFunction('hello world');
|
||||
const tokenEncryption = await createTokenEncryption();
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"declaration": false,
|
||||
@ -17,16 +17,13 @@
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
// TODO: Type errors
|
||||
"useUnknownInCatchVariables": false,
|
||||
// ES2022+ is currently broken https://github.com/matrix-org/matrix-hookshot/issues/729
|
||||
"target": "es2021"
|
||||
"useUnknownInCatchVariables": false
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
17
tsconfig.spec.json
Normal file
17
tsconfig.spec.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"incremental": false,
|
||||
"declaration": false,
|
||||
"outDir": "./spec-lib",
|
||||
"rootDir": "./",
|
||||
"allowJs": true
|
||||
},
|
||||
"include": [
|
||||
"spec/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"tests/**/*",
|
||||
"web/**/*"
|
||||
]
|
||||
}
|
@ -31,4 +31,11 @@ export default defineConfig({
|
||||
},
|
||||
emptyOutDir: true,
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
api: 'modern'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Component } from 'preact';
|
||||
import WA, { MatrixCapabilities } from 'matrix-widget-api';
|
||||
import { BridgeAPI, BridgeAPIError, EmbedType, embedTypeParameter } from './BridgeAPI';
|
||||
@ -84,8 +83,6 @@ export default class App extends Component<void, IState> {
|
||||
const roomState = widgetKind === "admin" ? await bridgeApi.state() : undefined;
|
||||
const supportedServices = await bridgeApi.getEnabledConfigSections();
|
||||
await widgetReady;
|
||||
// Calling setState is ok because we've awaited a network request.
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({
|
||||
userId,
|
||||
roomState,
|
||||
@ -106,8 +103,6 @@ export default class App extends Component<void, IState> {
|
||||
error = "Could not contact your homeserver. Your instance may be misconfigured.";
|
||||
}
|
||||
}
|
||||
// Calling setState is ok because we've awaited a network request.
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({
|
||||
error,
|
||||
busy: false,
|
||||
|
@ -46,7 +46,6 @@ export class BridgeAPI {
|
||||
|
||||
const creds = await widgetApi.requestOpenIDConnectToken();
|
||||
const { matrix_server_name, access_token } = creds;
|
||||
// eslint-disable-next-line camelcase
|
||||
if (!matrix_server_name || !access_token) {
|
||||
throw Error('Server OpenID response missing values');
|
||||
}
|
||||
@ -55,9 +54,7 @@ export class BridgeAPI {
|
||||
cache: 'no-cache',
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
// eslint-disable-next-line camelcase
|
||||
matrixServer: matrix_server_name,
|
||||
// eslint-disable-next-line camelcase
|
||||
openIdToken: access_token,
|
||||
} as ExchangeOpenAPIRequestBody),
|
||||
headers: {
|
||||
|
@ -36,23 +36,23 @@ export default function AdminSettings(props: IProps) {
|
||||
[setCurrentTab]
|
||||
);
|
||||
if (busy) {
|
||||
return <div class={style.root}>
|
||||
return <div className={style.root}>
|
||||
<LoadingSpinner />
|
||||
</div>;
|
||||
}
|
||||
return <div class={style.root}>
|
||||
<h1 class={style.header}> Hookshot Bridge settings</h1>
|
||||
<div class={style.contents}>
|
||||
<aside class={style.sidebar}>
|
||||
return <div className={style.root}>
|
||||
<h1 className={style.header}> Hookshot Bridge settings</h1>
|
||||
<div className={style.contents}>
|
||||
<aside className={style.sidebar}>
|
||||
<ul>
|
||||
{activeSections.general && <a sectionKey={AdminSettingsTab.General} onClick={onSectionClick}><li class={currentTab === AdminSettingsTab.General ? style.active : null}>General</li></a>}
|
||||
{activeSections.github && <a sectionKey={AdminSettingsTab.GitHub} onClick={onSectionClick}><li class={currentTab === AdminSettingsTab.GitHub ? style.active : null}> GitHub</li></a>}
|
||||
{activeSections.gitlab && <a sectionKey={AdminSettingsTab.GitLab} onClick={onSectionClick}><li class={currentTab === AdminSettingsTab.GitLab ? style.active : null}> GitLab</li></a>}
|
||||
{activeSections.jira && <a sectionKey={AdminSettingsTab.Jira} onClick={onSectionClick}><li class={currentTab === AdminSettingsTab.Jira ? style.active : null}> Jira</li></a>}
|
||||
{activeSections.figma && <a sectionKey={AdminSettingsTab.Figma} onClick={onSectionClick}><li class={currentTab === AdminSettingsTab.Figma ? style.active : null}> Figma</li></a>}
|
||||
{activeSections.general && <a onClick={onSectionClick}><li className={currentTab === AdminSettingsTab.General ? style.active : null}>General</li></a>}
|
||||
{activeSections.github && <a onClick={onSectionClick}><li className={currentTab === AdminSettingsTab.GitHub ? style.active : null}> GitHub</li></a>}
|
||||
{activeSections.gitlab && <a onClick={onSectionClick}><li className={currentTab === AdminSettingsTab.GitLab ? style.active : null}> GitLab</li></a>}
|
||||
{activeSections.jira && <a onClick={onSectionClick}><li className={currentTab === AdminSettingsTab.Jira ? style.active : null}> Jira</li></a>}
|
||||
{activeSections.figma && <a onClick={onSectionClick}><li className={currentTab === AdminSettingsTab.Figma ? style.active : null}> Figma</li></a>}
|
||||
</ul>
|
||||
</aside>
|
||||
<div class={style.content}>
|
||||
<div className={style.content}>
|
||||
{currentTab === AdminSettingsTab.General && <GeneralConfig />}
|
||||
{currentTab === AdminSettingsTab.GitHub && <GitHubState config={props.roomState.github} />}
|
||||
</div>
|
||||
|
@ -3,12 +3,12 @@ import { BridgeRoomStateGitHub } from '../../src/Widgets/BridgeWidgetInterface';
|
||||
import "./GitHubState.css";
|
||||
|
||||
const GitHubState: FunctionComponent<{config: BridgeRoomStateGitHub}> = ({ config }) => {
|
||||
return <div class="container login-card">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
return <div className="container login-card">
|
||||
<div className="row">
|
||||
<div className="col-sm-2">
|
||||
<img alt="GitHub avatar" src={config.identity.avatarUrl} />
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div className="col-sm-9">
|
||||
Logged in as <span>{config.identity.name}</span>
|
||||
<p>Notifications { config.notifications ? 'Enabled' : 'Disabled' }</p>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FunctionComponent, h } from "preact";
|
||||
import { FunctionComponent } from "preact";
|
||||
import style from "./ButtonSet.module.scss";
|
||||
|
||||
export const ButtonSet: FunctionComponent = (props) => {
|
||||
|
@ -13,7 +13,7 @@ export const EventHookCheckbox: FunctionComponent<{
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
x-event-name={hookEventName}
|
||||
data-event-name={hookEventName}
|
||||
checked={checked}
|
||||
onChange={onChange} />
|
||||
{ children }
|
||||
|
@ -51,7 +51,7 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ne
|
||||
|
||||
|
||||
const toggleEnabledHook = useCallback((evt: any) => {
|
||||
const key = (evt.target as HTMLElement).getAttribute('x-event-name');
|
||||
const key = (evt.target as HTMLElement).getAttribute('data-event-name');
|
||||
if (key) {
|
||||
setEnabledHooks(enabledHooks => (
|
||||
enabledHooks.includes(key) ? enabledHooks.filter(k => k !== key) : [...enabledHooks, key]
|
||||
|
@ -16,7 +16,7 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ne
|
||||
const api = useContext(BridgeContext).bridgeApi;
|
||||
|
||||
const toggleEnabledHook = useCallback((evt: any) => {
|
||||
const key = (evt.target as HTMLElement).getAttribute('x-event-name');
|
||||
const key = (evt.target as HTMLElement).getAttribute('data-event-name');
|
||||
if (key) {
|
||||
setEnabledHooks(enabledHooks => (
|
||||
enabledHooks.includes(key) ? enabledHooks.filter(k => k !== key) : [...enabledHooks, key]
|
||||
|
@ -17,7 +17,7 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ne
|
||||
const api = useContext(BridgeContext).bridgeApi;
|
||||
|
||||
const toggleEvent = useCallback((evt: Event) => {
|
||||
const key = (evt.target as HTMLElement).getAttribute('x-event-name');
|
||||
const key = (evt.target as HTMLElement).getAttribute('data-event-name');
|
||||
if (key) {
|
||||
setAllowedEvents(allowedEvents => (
|
||||
allowedEvents.includes(key) ? allowedEvents.filter(k => k !== key) : [...allowedEvents, key]
|
||||
|
Loading…
x
Reference in New Issue
Block a user