From 96f6e22bb6d82843ca82d914562ae1751195c26a Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 19 Oct 2023 23:10:47 +0200 Subject: [PATCH 01/20] Initial MSC1929 Implementation --- src/commands/QueryAdminDetails.tsx | 111 +++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/commands/QueryAdminDetails.tsx diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx new file mode 100644 index 00000000..332ae1d1 --- /dev/null +++ b/src/commands/QueryAdminDetails.tsx @@ -0,0 +1,111 @@ +import { Permalinks, UserID, getRequestFn } from "matrix-bot-sdk"; +import { MjolnirContext } from "./CommandHandler"; +import { CommandError, CommandResult } from "./interface-manager/Validation"; +import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand"; +import { findPresentationType, parameters, ParsedKeywords, union } from "./interface-manager/ParameterParsing"; +import "./interface-manager/MatrixPresentations"; +import { JSXFactory } from "./interface-manager/JSXFactory"; +import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor"; +import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix"; +import { DocumentNode } from "./interface-manager/DeadDocument"; + +export type MatrixHomeserver = string; + +interface SupportJson { + contacts?: { + matrix_id?: string, + email_address?: string; + role: "admin" | "security"; + }[], + support_page?: string; +} + +async function queryAdminDetails( + this: MjolnirContext, + _keywords: ParsedKeywords, + entity: UserID | MatrixHomeserver | string +): Promise> { + let domain: string; + if (entity instanceof UserID) { + domain = `https://${entity.domain}`; + } else { + // Doing some cleanup on the url + if (!entity.startsWith("https://") && !entity.startsWith("http://")) { + domain = `https://${entity}`; + } else if (entity.startsWith("http://")) { + domain = entity.replace("http://", "https://"); + } else { + domain = entity; + } + } + + + const resp: SupportJson = await new Promise((resolve, reject) => { + getRequestFn()(`${domain}/.well-known/matrix/support`, (error: any, response: any, resBody: unknown) => { + if (error || response.statusCode !== 200) { + reject(error); + } else if (typeof resBody === 'object' && resBody !== null && ('contacts' in resBody || 'support_page' in resBody)) { + resolve(resBody as SupportJson) + } else { + reject(new TypeError(`Don't know what to do with response body ${JSON.stringify(resBody)}. Assuming its not a json`)); + } + }); + }); + + return CommandResult.Ok([entity, resp]); +} + +defineInterfaceCommand({ + designator: ["queryAdmin"], + table: "mjolnir", + parameters: parameters([ + { + name: "entity", + acceptor: union( + findPresentationType("UserID"), + findPresentationType("MatrixHomeserver"), + findPresentationType("string") + ) + } + ]), + command: queryAdminDetails, + summary: "Queries the admin of the Homeserver or user using MSC1929 if available", +}) + +function renderSupportJson([entity, support_json]: [UserID | MatrixHomeserver | string, SupportJson],): DocumentNode { + if (!support_json.support_page) { + return + Support infos for ({entity}): + + + } else if (!support_json.contacts) { + return + Support Page for ({entity}): +

+ Support Page: {support_json.support_page} +

+
+ } else { + return + Support info for ({entity}): +

+ Support Page: {support_json.support_page} +

+ +
+ } +} + +defineMatrixInterfaceAdaptor({ + interfaceCommand: findTableCommand("mjolnir", "queryAdmin"), + renderer: async function (client, commandRoomId, event, result) { + await renderMatrixAndSend( + renderSupportJson(result.ok), + commandRoomId, event, client + ); + } +}) From 7dd0a86da5155a99a89dfd2b1390a79faecefacf Mon Sep 17 00:00:00 2001 From: MTRNord Date: Fri, 20 Oct 2023 12:46:34 +0200 Subject: [PATCH 02/20] Add missing import --- src/commands/CommandHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/CommandHandler.ts b/src/commands/CommandHandler.ts index f2212db3..229acbbd 100644 --- a/src/commands/CommandHandler.ts +++ b/src/commands/CommandHandler.ts @@ -73,6 +73,7 @@ import "./Rules"; import "./WatchUnwatchCommand"; import "./Help"; import "./SetDisplayNameCommand"; +import "./QueryAdminDetails"; export const COMMAND_PREFIX = "!mjolnir"; From 5f3c4000147da3f9566a566dd2e7f1905e7ad49e Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 10:48:49 +0200 Subject: [PATCH 03/20] Add missing makePresentationType --- src/commands/QueryAdminDetails.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 332ae1d1..451b6614 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -2,15 +2,22 @@ import { Permalinks, UserID, getRequestFn } from "matrix-bot-sdk"; import { MjolnirContext } from "./CommandHandler"; import { CommandError, CommandResult } from "./interface-manager/Validation"; import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand"; -import { findPresentationType, parameters, ParsedKeywords, union } from "./interface-manager/ParameterParsing"; +import { findPresentationType, makePresentationType, parameters, ParsedKeywords, simpleTypeValidator, union } from "./interface-manager/ParameterParsing"; import "./interface-manager/MatrixPresentations"; import { JSXFactory } from "./interface-manager/JSXFactory"; import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor"; import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix"; import { DocumentNode } from "./interface-manager/DeadDocument"; +import { ReadItem } from "./interface-manager/CommandReader"; export type MatrixHomeserver = string; +makePresentationType({ + name: "MatrixHomeserver", + // This is a very very crude way to detect a url. + validator: simpleTypeValidator("MatrixHomeserver", (readItem: ReadItem) => (readItem instanceof String) && (!readItem.includes('#') || !readItem.includes('!')) && !readItem.includes('.')) +}) + interface SupportJson { contacts?: { matrix_id?: string, From 48824f49c19c2918e9668474991e2af43b664cf7 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 11:06:57 +0200 Subject: [PATCH 04/20] Prepare nginx for queryAdmin command tests --- mx-tester.yml | 2 +- test/nginx.conf | 89 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/mx-tester.yml b/mx-tester.yml index 12e9e5b8..a249b61a 100644 --- a/mx-tester.yml +++ b/mx-tester.yml @@ -6,7 +6,7 @@ up: # Wait until postgresql is ready - until psql postgres://mjolnir-tester:mjolnir-test@127.0.0.1:8083/mjolnir-test-db -c ""; do echo "Waiting for psql..."; sleep 1s; done # Launch the reverse proxy, listening for connections *only* on the local host. - - docker run --rm --network host --name mjolnir-test-reverse-proxy -p 127.0.0.1:8081:80 -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx + - docker run --rm --network host --name mjolnir-test-reverse-proxy -p 127.0.0.1:8084:8084 -p 127.0.0.1:8085:8085 -p 127.0.0.1:8086:8086 -p 127.0.0.1:8081:8081 -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx - yarn install - npx ts-node src/appservice/cli.ts -r -u "http://host.docker.internal:9000" - cp mjolnir-registration.yaml $MX_TEST_SYNAPSE_DIR/data/ diff --git a/test/nginx.conf b/test/nginx.conf index 23c73c76..9892e443 100644 --- a/test/nginx.conf +++ b/test/nginx.conf @@ -4,29 +4,74 @@ events { http { server { - listen [::]:8081 ipv6only=off; - - location ~ ^/_matrix/client/(r0|v3)/rooms/([^/]*)/report/(.*)$ { - # Abuse reports should be sent to Mjölnir. - # The r0 endpoint is deprecated but still used by many clients. - # As of this writing, the v3 endpoint is the up-to-date version. - - # Add CORS, otherwise a browser will refuse this request. - add_header 'Access-Control-Allow-Origin' '*' always; # Note: '*' is for testing purposes. For your own server, you probably want to tighten this. - add_header 'Access-Control-Allow-Credentials' 'true' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; - add_header 'Access-Control-Max-Age' 1728000; # cache preflight value for 20 days - - # Alias the regexps, to ensure that they're not rewritten. - set $room_id $2; - set $event_id $3; - proxy_pass http://127.0.0.1:8082/api/1/report/$room_id/$event_id; + listen [::]:8081 ipv6only=off; + + location ~ ^/_matrix/client/(r0|v3)/rooms/([^/]*)/report/(.*)$ { + # Abuse reports should be sent to Mjölnir. + # The r0 endpoint is deprecated but still used by many clients. + # As of this writing, the v3 endpoint is the up-to-date version. + + # Add CORS, otherwise a browser will refuse this request. + add_header 'Access-Control-Allow-Origin' '*' always; # Note: '*' is for testing purposes. For your own server, you probably want to tighten this. + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; + add_header 'Access-Control-Max-Age' 1728000; # cache preflight value for 20 days + + # Alias the regexps, to ensure that they're not rewritten. + set $room_id $2; + set $event_id $3; + proxy_pass http://127.0.0.1:8082/api/1/report/$room_id/$event_id; + } + # MSC1929 Test in best-case + location /.well-known/matrix/support { + default_type application/json; + return 200 '{ + "contacts": [{ + "matrix_id": "@admin:localhost", + "role": "admin" + }], + "support_page": "http://localhost" + }'; + } + location / { + # Everything else should be sent to Synapse. + proxy_pass http://127.0.0.1:9999; + } + } + server { + listen [::]:8084 ipv6only=off; + + # MSC1929 Test in worst-case + location /.well-known/matrix/support { + default_type application/json; + return 404 '{}'; + } + } + server { + listen [::]:8085 ipv6only=off; + + # MSC1929 Test in supportpage_only-case + location /.well-known/matrix/support { + default_type application/json; + return 200 '{ + "support_page": "http://localhost" + }'; + } } - location / { - # Everything else should be sent to Synapse. - proxy_pass http://127.0.0.1:9999; + server { + listen [::]:8086 ipv6only=off; + + # MSC1929 Test in contacts-case + location /.well-known/matrix/support { + default_type application/json; + return 200 '{ + "contacts": [{ + "matrix_id": "@admin:localhost", + "role": "admin" + }], + }'; } } } From 0ac0b44bd4e07cb41cb1e19aa2de7daf5a2085b9 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 11:21:37 +0200 Subject: [PATCH 05/20] Fix tests --- mx-tester.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mx-tester.yml b/mx-tester.yml index a249b61a..1e903e89 100644 --- a/mx-tester.yml +++ b/mx-tester.yml @@ -6,7 +6,7 @@ up: # Wait until postgresql is ready - until psql postgres://mjolnir-tester:mjolnir-test@127.0.0.1:8083/mjolnir-test-db -c ""; do echo "Waiting for psql..."; sleep 1s; done # Launch the reverse proxy, listening for connections *only* on the local host. - - docker run --rm --network host --name mjolnir-test-reverse-proxy -p 127.0.0.1:8084:8084 -p 127.0.0.1:8085:8085 -p 127.0.0.1:8086:8086 -p 127.0.0.1:8081:8081 -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx + - docker run --rm --network host --name mjolnir-test-reverse-proxy -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx - yarn install - npx ts-node src/appservice/cli.ts -r -u "http://host.docker.internal:9000" - cp mjolnir-registration.yaml $MX_TEST_SYNAPSE_DIR/data/ From 06960231e2f62baa93fe7c52139243f4b7121c69 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 11:35:32 +0200 Subject: [PATCH 06/20] Fixup reverse proxy (maybe) --- mx-tester.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mx-tester.yml b/mx-tester.yml index 1e903e89..bccaec3d 100644 --- a/mx-tester.yml +++ b/mx-tester.yml @@ -6,7 +6,7 @@ up: # Wait until postgresql is ready - until psql postgres://mjolnir-tester:mjolnir-test@127.0.0.1:8083/mjolnir-test-db -c ""; do echo "Waiting for psql..."; sleep 1s; done # Launch the reverse proxy, listening for connections *only* on the local host. - - docker run --rm --network host --name mjolnir-test-reverse-proxy -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx + - docker run --rm --network host --name mjolnir-test-reverse-proxy -p 127.0.0.1:8084:8084 -p 127.0.0.1:8085:8085 -p 127.0.0.1:8086:8086 -p 127.0.0.1:8081:80 -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx - yarn install - npx ts-node src/appservice/cli.ts -r -u "http://host.docker.internal:9000" - cp mjolnir-registration.yaml $MX_TEST_SYNAPSE_DIR/data/ From 97fcdb9a0b135848529f616083f884a5f0d50ba9 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 11:43:33 +0200 Subject: [PATCH 07/20] Use ports that are further away --- mx-tester.yml | 2 +- test/nginx.conf | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mx-tester.yml b/mx-tester.yml index bccaec3d..1e903e89 100644 --- a/mx-tester.yml +++ b/mx-tester.yml @@ -6,7 +6,7 @@ up: # Wait until postgresql is ready - until psql postgres://mjolnir-tester:mjolnir-test@127.0.0.1:8083/mjolnir-test-db -c ""; do echo "Waiting for psql..."; sleep 1s; done # Launch the reverse proxy, listening for connections *only* on the local host. - - docker run --rm --network host --name mjolnir-test-reverse-proxy -p 127.0.0.1:8084:8084 -p 127.0.0.1:8085:8085 -p 127.0.0.1:8086:8086 -p 127.0.0.1:8081:80 -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx + - docker run --rm --network host --name mjolnir-test-reverse-proxy -v $MX_TEST_CWD/test/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx - yarn install - npx ts-node src/appservice/cli.ts -r -u "http://host.docker.internal:9000" - cp mjolnir-registration.yaml $MX_TEST_SYNAPSE_DIR/data/ diff --git a/test/nginx.conf b/test/nginx.conf index 9892e443..943d8e84 100644 --- a/test/nginx.conf +++ b/test/nginx.conf @@ -6,7 +6,7 @@ http { server { listen [::]:8081 ipv6only=off; - location ~ ^/_matrix/client/(r0|v3)/rooms/([^/]*)/report/(.*)$ { + location ~ ^/_matrix/client/(r0|v3)/rooms/([^/\s]*)/report/(.*)$ { # Abuse reports should be sent to Mjölnir. # The r0 endpoint is deprecated but still used by many clients. # As of this writing, the v3 endpoint is the up-to-date version. @@ -41,7 +41,7 @@ http { } } server { - listen [::]:8084 ipv6only=off; + listen [::]:7070 ipv6only=off; # MSC1929 Test in worst-case location /.well-known/matrix/support { @@ -50,7 +50,7 @@ http { } } server { - listen [::]:8085 ipv6only=off; + listen [::]:7071 ipv6only=off; # MSC1929 Test in supportpage_only-case location /.well-known/matrix/support { @@ -61,7 +61,7 @@ http { } } server { - listen [::]:8086 ipv6only=off; + listen [::]:7072 ipv6only=off; # MSC1929 Test in contacts-case location /.well-known/matrix/support { From b5c8e988f84608f8e307ddc776e276bb876e01a0 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 12:00:39 +0200 Subject: [PATCH 08/20] Add basics tests for queryAdmin command --- src/commands/QueryAdminDetails.tsx | 2 +- .../commands/queryAdminDetailsTest.ts | 89 +++++++++++++++++++ tsconfig.json | 1 + 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 test/integration/commands/queryAdminDetailsTest.ts diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 451b6614..06077afd 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -15,7 +15,7 @@ export type MatrixHomeserver = string; makePresentationType({ name: "MatrixHomeserver", // This is a very very crude way to detect a url. - validator: simpleTypeValidator("MatrixHomeserver", (readItem: ReadItem) => (readItem instanceof String) && (!readItem.includes('#') || !readItem.includes('!')) && !readItem.includes('.')) + validator: simpleTypeValidator("MatrixHomeserver", (readItem: ReadItem) => (readItem instanceof String) && (!readItem.includes('#') || !readItem.includes('!'))) }) interface SupportJson { diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts new file mode 100644 index 00000000..bcf750d9 --- /dev/null +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -0,0 +1,89 @@ +import { strict as assert } from "assert"; + +import { newTestUser } from "../clientHelper"; +import { getMessagesByUserIn } from "../../../src/utils"; + +describe("Test: The queryAdmin command", function () { + // If a test has a timeout while awaitng on a promise then we never get given control back. + afterEach(function () { this.moderator?.stop(); }); + + it('Mjölnir can query and display the query results for a complete json.', async function () { + let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + this.moderator = moderator; + await moderator.joinRoom(this.config.managementRoom); + moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:8081` }); + + + const draupnir = this.config.RUNTIME.client! + let draupnirUserId = await draupnir.getUserId(); + + // Check if draupnir replied + await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { + events.map(e => { + if (e.type === 'm.room.message') { + assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + } + }) + }); + }) + + it('Mjölnir can query and display the query results for a partial contacts-only json.', async function () { + let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + this.moderator = moderator; + await moderator.joinRoom(this.config.managementRoom); + moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7072` }); + + + const draupnir = this.config.RUNTIME.client! + let draupnirUserId = await draupnir.getUserId(); + + // Check if draupnir replied + await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { + events.map(e => { + if (e.type === 'm.room.message') { + assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + } + }) + }); + }) + + it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () { + let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + this.moderator = moderator; + await moderator.joinRoom(this.config.managementRoom); + moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7071` }); + + + const draupnir = this.config.RUNTIME.client! + let draupnirUserId = await draupnir.getUserId(); + + // Check if draupnir replied + await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { + events.map(e => { + if (e.type === 'm.room.message') { + assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + } + }) + }); + }) + + it('Mjölnir can query and display an error for a non well-formed json.', async function () { + let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + this.moderator = moderator; + await moderator.joinRoom(this.config.managementRoom); + moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7070` }); + + + const draupnir = this.config.RUNTIME.client! + let draupnirUserId = await draupnir.getUserId(); + + // Check if draupnir replied + await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { + events.map(e => { + if (e.type === 'm.room.message') { + assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + } + }) + }); + }) +}); diff --git a/tsconfig.json b/tsconfig.json index 046c87d5..b9ddfff7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,5 +33,6 @@ "./test/integration/protectionSettingsTest.ts", "./test/integration/banPropagationTest.ts", "./test/integration/protectedRoomsConfigTest.ts", + "./test/integration/commands/queryAdminDetailsTest.ts", ] } From 7563a443a97bad7e8db3202385fc78c7969f3b61 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 12:09:15 +0200 Subject: [PATCH 09/20] Fix typo --- test/integration/commands/queryAdminDetailsTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index bcf750d9..0049c35e 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -21,7 +21,7 @@ describe("Test: The queryAdmin command", function () { await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { events.map(e => { if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) } }) }); @@ -41,7 +41,7 @@ describe("Test: The queryAdmin command", function () { await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { events.map(e => { if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) } }) }); @@ -61,7 +61,7 @@ describe("Test: The queryAdmin command", function () { await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { events.map(e => { if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) } }) }); @@ -81,7 +81,7 @@ describe("Test: The queryAdmin command", function () { await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { events.map(e => { if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draipnir did not parse the json as expected: ${e.content.body}.`) + assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) } }) }); From 7e6c0edef53341c9dcfa50bb29476c19c13c1185 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 12:15:12 +0200 Subject: [PATCH 10/20] Fix event handler for queryAdmin Test --- .../commands/queryAdminDetailsTest.ts | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index 0049c35e..9b06c450 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -1,89 +1,85 @@ import { strict as assert } from "assert"; -import { newTestUser } from "../clientHelper"; -import { getMessagesByUserIn } from "../../../src/utils"; +import { newTestUser, noticeListener } from "../clientHelper"; describe("Test: The queryAdmin command", function () { // If a test has a timeout while awaitng on a promise then we never get given control back. afterEach(function () { this.moderator?.stop(); }); it('Mjölnir can query and display the query results for a complete json.', async function () { - let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:8081` }); + // listener for getting the event reply + const reply = new Promise((resolve, reject) => { + moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { + resolve(event); + })) + }); - const draupnir = this.config.RUNTIME.client! - let draupnirUserId = await draupnir.getUserId(); + await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:8081` }); - // Check if draupnir replied - await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { - events.map(e => { - if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) - } - }) - }); + + const reply_event = await reply; + + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) }) it('Mjölnir can query and display the query results for a partial contacts-only json.', async function () { - let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7072` }); + // listener for getting the event reply + const reply = new Promise((resolve, reject) => { + moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { + resolve(event); + })) + }); - const draupnir = this.config.RUNTIME.client! - let draupnirUserId = await draupnir.getUserId(); + await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7072` }); - // Check if draupnir replied - await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { - events.map(e => { - if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) - } - }) - }); + const reply_event = await reply; + + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) }) it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () { - let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7071` }); + // listener for getting the event reply + const reply = new Promise((resolve, reject) => { + moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { + resolve(event); + })) + }); - const draupnir = this.config.RUNTIME.client! - let draupnirUserId = await draupnir.getUserId(); + await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7071` }); - // Check if draupnir replied - await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { - events.map(e => { - if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) - } - }) - }); + const reply_event = await reply; + + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) }) it('Mjölnir can query and display an error for a non well-formed json.', async function () { - let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); + const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } }); this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7070` }); + // listener for getting the event reply + const reply = new Promise((resolve, reject) => { + moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { + resolve(event); + })) + }); - const draupnir = this.config.RUNTIME.client! - let draupnirUserId = await draupnir.getUserId(); + moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7070` }); - // Check if draupnir replied - await getMessagesByUserIn(moderator, draupnirUserId, this.mjolnir.managementRoomId, 1000, function (events) { - events.map(e => { - if (e.type === 'm.room.message') { - assert.equal(e.content.body, "", `Draupnir did not parse the json as expected: ${e.content.body}.`) - } - }) - }); + const reply_event = await reply; + + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) }) }); From 8a888e282406887a0c317f5b768f3421057b3b1e Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 12:38:47 +0200 Subject: [PATCH 11/20] Fix test reliability --- .../commands/queryAdminDetailsTest.ts | 60 +++++-------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index 9b06c450..23136f94 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -1,6 +1,7 @@ import { strict as assert } from "assert"; -import { newTestUser, noticeListener } from "../clientHelper"; +import { newTestUser } from "../clientHelper"; +import { getFirstReply } from "./commandUtils"; describe("Test: The queryAdmin command", function () { // If a test has a timeout while awaitng on a promise then we never get given control back. @@ -11,19 +12,10 @@ describe("Test: The queryAdmin command", function () { this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - // listener for getting the event reply - const reply = new Promise((resolve, reject) => { - moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { - resolve(event); - })) + const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:8081` }); }); - - await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:8081` }); - - - const reply_event = await reply; - - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); }) it('Mjölnir can query and display the query results for a partial contacts-only json.', async function () { @@ -31,18 +23,10 @@ describe("Test: The queryAdmin command", function () { this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - // listener for getting the event reply - const reply = new Promise((resolve, reject) => { - moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { - resolve(event); - })) + const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7072` }); }); - - await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7072` }); - - const reply_event = await reply; - - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); }) it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () { @@ -50,18 +34,10 @@ describe("Test: The queryAdmin command", function () { this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - // listener for getting the event reply - const reply = new Promise((resolve, reject) => { - moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { - resolve(event); - })) + const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7071` }); }); - - await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7071` }); - - const reply_event = await reply; - - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); }) it('Mjölnir can query and display an error for a non well-formed json.', async function () { @@ -69,17 +45,9 @@ describe("Test: The queryAdmin command", function () { this.moderator = moderator; await moderator.joinRoom(this.config.managementRoom); - // listener for getting the event reply - const reply = new Promise((resolve, reject) => { - moderator.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => { - resolve(event); - })) + const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7070` }); }); - - moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir queryAdmin http://localhost:7070` }); - - const reply_event = await reply; - - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`) + assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); }) }); From 05b37d86279a5ad5bb2fdff1b053df2b7d9b4c05 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 13:49:51 +0200 Subject: [PATCH 12/20] Do not upgrade http to https --- src/commands/QueryAdminDetails.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 06077afd..510cd9ff 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -39,8 +39,6 @@ async function queryAdminDetails( // Doing some cleanup on the url if (!entity.startsWith("https://") && !entity.startsWith("http://")) { domain = `https://${entity}`; - } else if (entity.startsWith("http://")) { - domain = entity.replace("http://", "https://"); } else { domain = entity; } From 147d7737afff6e39909f598e7987686a2559b783 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:04:23 +0200 Subject: [PATCH 13/20] Improve request call and error handling --- src/commands/QueryAdminDetails.tsx | 31 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 510cd9ff..21232b24 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -45,19 +45,26 @@ async function queryAdminDetails( } - const resp: SupportJson = await new Promise((resolve, reject) => { - getRequestFn()(`${domain}/.well-known/matrix/support`, (error: any, response: any, resBody: unknown) => { - if (error || response.statusCode !== 200) { - reject(error); - } else if (typeof resBody === 'object' && resBody !== null && ('contacts' in resBody || 'support_page' in resBody)) { - resolve(resBody as SupportJson) - } else { - reject(new TypeError(`Don't know what to do with response body ${JSON.stringify(resBody)}. Assuming its not a json`)); - } + try { + const resp: SupportJson = await new Promise((resolve, reject) => { + getRequestFn()(`${domain}/.well-known/matrix/support`, (error: any, response: any, resBody: string) => { + if (error) { + reject(new CommandError(`The request failed with an error: ${error}.`)); + } else if (response.statusCode !== 200) { + reject(new CommandError(`The server didn't reply with a valid response code: ${response.statusCode}.`)); + } else if (resBody !== null && (resBody.includes('contacts') || resBody.includes('support_page'))) { + resolve(JSON.parse(resBody) as SupportJson) + } else if (resBody === null) { + reject(new CommandError(`The response was empty.`)); + } else { + reject(new CommandError(`Don't know what to do with response body ${resBody}. Assuming its not a json`)); + } + }); }); - }); - - return CommandResult.Ok([entity, resp]); + return CommandResult.Ok([entity, resp]); + } catch (error: any) { + return CommandResult.Err(error); + } } defineInterfaceCommand({ From d40647d7f79922be05907f9ddb70fbf5c9b24385 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:05:42 +0200 Subject: [PATCH 14/20] Remove extra comma --- test/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/nginx.conf b/test/nginx.conf index 943d8e84..2885f153 100644 --- a/test/nginx.conf +++ b/test/nginx.conf @@ -70,7 +70,7 @@ http { "contacts": [{ "matrix_id": "@admin:localhost", "role": "admin" - }], + }] }'; } } From 948c9958554012a83481591ac3c59242900bda8a Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:07:26 +0200 Subject: [PATCH 15/20] Fix newlines --- src/commands/QueryAdminDetails.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 21232b24..7d7ce3b2 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -87,26 +87,26 @@ defineInterfaceCommand({ function renderSupportJson([entity, support_json]: [UserID | MatrixHomeserver | string, SupportJson],): DocumentNode { if (!support_json.support_page) { return - Support infos for ({entity}): + Support infos for ({entity}):
} else if (!support_json.contacts) { return - Support Page for ({entity}): + Support Page for ({entity}):

Support Page: {support_json.support_page}

} else { return - Support info for ({entity}): + Support info for ({entity}):

Support Page: {support_json.support_page} -

+


} From 830b6fc2b7f0329d0d2867621f753d2f00afea0c Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:15:05 +0200 Subject: [PATCH 16/20] Revert json parsing changes --- src/commands/QueryAdminDetails.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 7d7ce3b2..33da21ed 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -47,13 +47,13 @@ async function queryAdminDetails( try { const resp: SupportJson = await new Promise((resolve, reject) => { - getRequestFn()(`${domain}/.well-known/matrix/support`, (error: any, response: any, resBody: string) => { + getRequestFn()(`${domain}/.well-known/matrix/support`, (error: any, response: any, resBody: unknown) => { if (error) { reject(new CommandError(`The request failed with an error: ${error}.`)); } else if (response.statusCode !== 200) { reject(new CommandError(`The server didn't reply with a valid response code: ${response.statusCode}.`)); - } else if (resBody !== null && (resBody.includes('contacts') || resBody.includes('support_page'))) { - resolve(JSON.parse(resBody) as SupportJson) + } else if (typeof resBody === 'object' && resBody !== null && ('contacts' in resBody || 'support_page' in resBody)) { + resolve(resBody as SupportJson) } else if (resBody === null) { reject(new CommandError(`The response was empty.`)); } else { From faee925aff23da5605333b5dd99ff49b5bfae329 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:27:53 +0200 Subject: [PATCH 17/20] Fix asserts --- test/integration/commands/queryAdminDetailsTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index 23136f94..f4b6c4e4 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -15,7 +15,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:8081` }); }); - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); + assert.equal(reply_event.content.body, "**Support info for (http://localhost:8081):**\nSupport Page: http://localhost\n\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display the query results for a partial contacts-only json.', async function () { @@ -26,7 +26,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7072` }); }); - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); + assert.equal(reply_event.content.body, "**Support infos for (http://localhost:7072):**\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () { @@ -37,7 +37,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7071` }); }); - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); + assert.equal(reply_event.content.body, "**Support Page for (http://localhost:7071):**\nSupport Page: http://localhost\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display an error for a non well-formed json.', async function () { @@ -48,6 +48,6 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7070` }); }); - assert.equal(reply_event.content.body, "", `Draupnir did not parse the json as expected: ${reply_event.content.body}.`); + assert.equal(reply_event.content.body, "> <@mjolnir-test-user-moderator61610:localhost:9999> !mjolnir queryAdmin http://localhost:7070\nThe request failed with an error: Error: Error during MatrixClient request GET /.well-known/matrix/support: 404 Not Found -- {}.", `Draupnir did not parse the json as expected.`); }) }); From 179e74a84549d61aaea36e180eeeb4389fd52e78 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 24 Oct 2023 14:39:55 +0200 Subject: [PATCH 18/20] Fix newlines --- test/integration/commands/queryAdminDetailsTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index f4b6c4e4..c3493c8b 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -15,7 +15,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:8081` }); }); - assert.equal(reply_event.content.body, "**Support info for (http://localhost:8081):**\nSupport Page: http://localhost\n\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n", `Draupnir did not parse the json as expected.`); + assert.equal(reply_event.content.body, "**Support info for (http://localhost:8081):**\nSupport Page: http://localhost\n\n\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display the query results for a partial contacts-only json.', async function () { @@ -26,7 +26,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7072` }); }); - assert.equal(reply_event.content.body, "**Support infos for (http://localhost:7072):**\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n", `Draupnir did not parse the json as expected.`); + assert.equal(reply_event.content.body, "**Support infos for (http://localhost:7072):**\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () { @@ -37,7 +37,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7071` }); }); - assert.equal(reply_event.content.body, "**Support Page for (http://localhost:7071):**\nSupport Page: http://localhost\n", `Draupnir did not parse the json as expected.`); + assert.equal(reply_event.content.body, "**Support Page for (http://localhost:7071):**\nSupport Page: http://localhost\n\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display an error for a non well-formed json.', async function () { @@ -48,6 +48,6 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7070` }); }); - assert.equal(reply_event.content.body, "> <@mjolnir-test-user-moderator61610:localhost:9999> !mjolnir queryAdmin http://localhost:7070\nThe request failed with an error: Error: Error during MatrixClient request GET /.well-known/matrix/support: 404 Not Found -- {}.", `Draupnir did not parse the json as expected.`); + assert.equal(reply_event.content.body.includes("The request failed with an error: Error: Error during MatrixClient request GET /.well-known/matrix/support:"), true, `Draupnir did not print an error as.`); }) }); From 506ae54ed926b32b3ba6cd6ced54f5122db173e0 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 31 Oct 2023 15:16:52 +0100 Subject: [PATCH 19/20] Change the command `queryAdmin` to `query admin` and apply suggestions for the representation type. Co-authored-by: gnuxie --- src/commands/QueryAdminDetails.tsx | 15 ++++++++++----- .../integration/commands/queryAdminDetailsTest.ts | 8 ++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/commands/QueryAdminDetails.tsx b/src/commands/QueryAdminDetails.tsx index 33da21ed..c64645b9 100644 --- a/src/commands/QueryAdminDetails.tsx +++ b/src/commands/QueryAdminDetails.tsx @@ -10,12 +10,17 @@ import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix"; import { DocumentNode } from "./interface-manager/DeadDocument"; import { ReadItem } from "./interface-manager/CommandReader"; -export type MatrixHomeserver = string; +const MatrixHomeserverSecret = Symbol("MatrixHomeserverSecret"); +export type MatrixHomeserver = string & { [MatrixHomeserverSecret]: true }; + +function isMatrixHomeserver(string: string): string is MatrixHomeserver { + return !string.includes('#') && !string.includes('!') +} makePresentationType({ name: "MatrixHomeserver", // This is a very very crude way to detect a url. - validator: simpleTypeValidator("MatrixHomeserver", (readItem: ReadItem) => (readItem instanceof String) && (!readItem.includes('#') || !readItem.includes('!'))) + validator: simpleTypeValidator("MatrixHomeserver", (readItem: ReadItem) => (typeof readItem === 'string') && isMatrixHomeserver(readItem)) }) interface SupportJson { @@ -68,7 +73,7 @@ async function queryAdminDetails( } defineInterfaceCommand({ - designator: ["queryAdmin"], + designator: ["query", "admin"], table: "mjolnir", parameters: parameters([ { @@ -87,7 +92,7 @@ defineInterfaceCommand({ function renderSupportJson([entity, support_json]: [UserID | MatrixHomeserver | string, SupportJson],): DocumentNode { if (!support_json.support_page) { return - Support infos for ({entity}):
+ Support info for ({entity}):
@@ -113,7 +118,7 @@ function renderSupportJson([entity, support_json]: [UserID | MatrixHomeserver | } defineMatrixInterfaceAdaptor({ - interfaceCommand: findTableCommand("mjolnir", "queryAdmin"), + interfaceCommand: findTableCommand("mjolnir", "query", "admin"), renderer: async function (client, commandRoomId, event, result) { await renderMatrixAndSend( renderSupportJson(result.ok), diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index c3493c8b..af80ef84 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -13,7 +13,7 @@ describe("Test: The queryAdmin command", function () { await moderator.joinRoom(this.config.managementRoom); const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { - return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:8081` }); + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir query admin http://localhost:8081` }); }); assert.equal(reply_event.content.body, "**Support info for (http://localhost:8081):**\nSupport Page: http://localhost\n\n\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); }) @@ -24,7 +24,7 @@ describe("Test: The queryAdmin command", function () { await moderator.joinRoom(this.config.managementRoom); const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { - return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7072` }); + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir query admin http://localhost:7072` }); }); assert.equal(reply_event.content.body, "**Support infos for (http://localhost:7072):**\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); }) @@ -35,7 +35,7 @@ describe("Test: The queryAdmin command", function () { await moderator.joinRoom(this.config.managementRoom); const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { - return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7071` }); + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir query admin http://localhost:7071` }); }); assert.equal(reply_event.content.body, "**Support Page for (http://localhost:7071):**\nSupport Page: http://localhost\n\n", `Draupnir did not parse the json as expected.`); }) @@ -46,7 +46,7 @@ describe("Test: The queryAdmin command", function () { await moderator.joinRoom(this.config.managementRoom); const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { - return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir queryAdmin http://localhost:7070` }); + return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir query admin http://localhost:7070` }); }); assert.equal(reply_event.content.body.includes("The request failed with an error: Error: Error during MatrixClient request GET /.well-known/matrix/support:"), true, `Draupnir did not print an error as.`); }) From 86a73efce03107c5b290beebac3e03354fc0dc77 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 31 Oct 2023 15:30:22 +0100 Subject: [PATCH 20/20] Update the test to reflect the type change --- test/integration/commands/queryAdminDetailsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/commands/queryAdminDetailsTest.ts b/test/integration/commands/queryAdminDetailsTest.ts index af80ef84..4f536090 100644 --- a/test/integration/commands/queryAdminDetailsTest.ts +++ b/test/integration/commands/queryAdminDetailsTest.ts @@ -26,7 +26,7 @@ describe("Test: The queryAdmin command", function () { const reply_event = await getFirstReply(this.mjolnir.matrixEmitter, this.mjolnir.managementRoomId, async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir query admin http://localhost:7072` }); }); - assert.equal(reply_event.content.body, "**Support infos for (http://localhost:7072):**\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); + assert.equal(reply_event.content.body, "**Support info for (http://localhost:7072):**\n\n * **admin** - [@admin:localhost](https://matrix.to/#/@admin:localhost)\n\n", `Draupnir did not parse the json as expected.`); }) it('Mjölnir can query and display the query results for a partial support_page-only json.', async function () {