Add actions for integration manager to send and read certain events (#9740)
* Add send_event action * Add read_events action * Add e2e tests for send_event * Allow more event types to be read * Fix error message * Add e2e tests for read_events * Run i18n * Improve error messages * Fix types * Remove unused test helpers * Fix more type errors * Run prettier * Fix more type errors * Fix validation Since typeof null is "object" Co-authored-by: Travis Ralston <travisr@matrix.org>
This commit is contained in:
parent
d6e447b934
commit
6585fb1f55
4 changed files with 711 additions and 3 deletions
276
cypress/e2e/integration-manager/read_events.spec.ts
Normal file
276
cypress/e2e/integration-manager/read_events.spec.ts
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||||
|
import { UserCredentials } from "../../support/login";
|
||||||
|
|
||||||
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
const USER_DISPLAY_NAME = "Alice";
|
||||||
|
|
||||||
|
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||||
|
const INTEGRATION_MANAGER_HTML = `
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Fake Integration Manager</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<input type="text" id="target-room-id"/>
|
||||||
|
<input type="text" id="event-type"/>
|
||||||
|
<input type="text" id="state-key"/>
|
||||||
|
<button name="Send" id="send-action">Press to send action</button>
|
||||||
|
<button name="Close" id="close">Press to close</button>
|
||||||
|
<p id="message-response">No response</p>
|
||||||
|
<script>
|
||||||
|
document.getElementById("send-action").onclick = () => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
action: "read_events",
|
||||||
|
room_id: document.getElementById("target-room-id").value,
|
||||||
|
type: document.getElementById("event-type").value,
|
||||||
|
state_key: JSON.parse(document.getElementById("state-key").value),
|
||||||
|
},
|
||||||
|
'*',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
document.getElementById("close").onclick = () => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
action: "close_scalar",
|
||||||
|
},
|
||||||
|
'*',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Listen for a postmessage response
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
document.getElementById("message-response").innerText = JSON.stringify(event.data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
function openIntegrationManager() {
|
||||||
|
cy.get(".mx_RightPanel_roomSummaryButton").click();
|
||||||
|
cy.get(".mx_RoomSummaryCard_appsGroup").within(() => {
|
||||||
|
cy.contains("Add widgets, bridges & bots").click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendActionFromIntegrationManager(
|
||||||
|
integrationManagerUrl: string,
|
||||||
|
targetRoomId: string,
|
||||||
|
eventType: string,
|
||||||
|
stateKey: string | boolean,
|
||||||
|
) {
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#target-room-id").should("exist").type(targetRoomId);
|
||||||
|
cy.get("#event-type").should("exist").type(eventType);
|
||||||
|
cy.get("#state-key").should("exist").type(JSON.stringify(stateKey));
|
||||||
|
cy.get("#send-action").should("exist").click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Integration Manager: Read Events", () => {
|
||||||
|
let testUser: UserCredentials;
|
||||||
|
let synapse: SynapseInstance;
|
||||||
|
let integrationManagerUrl: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||||
|
integrationManagerUrl = url;
|
||||||
|
});
|
||||||
|
cy.startSynapse("default").then((data) => {
|
||||||
|
synapse = data;
|
||||||
|
|
||||||
|
cy.initTestUser(synapse, USER_DISPLAY_NAME, () => {
|
||||||
|
cy.window().then((win) => {
|
||||||
|
win.localStorage.setItem("mx_scalar_token", INTEGRATION_MANAGER_TOKEN);
|
||||||
|
win.localStorage.setItem(`mx_scalar_token_at_${integrationManagerUrl}`, INTEGRATION_MANAGER_TOKEN);
|
||||||
|
});
|
||||||
|
}).then((user) => {
|
||||||
|
testUser = user;
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.setAccountData("m.widgets", {
|
||||||
|
"m.integration_manager": {
|
||||||
|
content: {
|
||||||
|
type: "m.integration_manager",
|
||||||
|
name: "Integration Manager",
|
||||||
|
url: integrationManagerUrl,
|
||||||
|
data: {
|
||||||
|
api_url: integrationManagerUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "integration-manager",
|
||||||
|
},
|
||||||
|
}).as("integrationManager");
|
||||||
|
|
||||||
|
// Succeed when checking the token is valid
|
||||||
|
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||||
|
req.continue((res) => {
|
||||||
|
return res.send(200, {
|
||||||
|
user_id: testUser.userId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.createRoom({
|
||||||
|
name: ROOM_NAME,
|
||||||
|
}).as("roomId");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cy.stopSynapse(synapse);
|
||||||
|
cy.stopWebServers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should read a state event by state key", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
const eventContent = {
|
||||||
|
foo: "bar",
|
||||||
|
};
|
||||||
|
const stateKey = "state-key-123";
|
||||||
|
|
||||||
|
// Send a state event
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return await client.sendStateEvent(roomId, eventType, eventContent, stateKey);
|
||||||
|
})
|
||||||
|
.then((event) => {
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
// Read state events
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response")
|
||||||
|
.should("include.text", event.event_id)
|
||||||
|
.should("include.text", `"content":${JSON.stringify(eventContent)}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should read a state event with empty state key", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
const eventContent = {
|
||||||
|
foo: "bar",
|
||||||
|
};
|
||||||
|
const stateKey = "";
|
||||||
|
|
||||||
|
// Send a state event
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return await client.sendStateEvent(roomId, eventType, eventContent, stateKey);
|
||||||
|
})
|
||||||
|
.then((event) => {
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
// Read state events
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response")
|
||||||
|
.should("include.text", event.event_id)
|
||||||
|
.should("include.text", `"content":${JSON.stringify(eventContent)}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should read state events with any state key", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
|
||||||
|
const stateKey1 = "state-key-123";
|
||||||
|
const eventContent1 = {
|
||||||
|
foo1: "bar1",
|
||||||
|
};
|
||||||
|
const stateKey2 = "state-key-456";
|
||||||
|
const eventContent2 = {
|
||||||
|
foo2: "bar2",
|
||||||
|
};
|
||||||
|
const stateKey3 = "state-key-789";
|
||||||
|
const eventContent3 = {
|
||||||
|
foo3: "bar3",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send state events
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return Promise.all([
|
||||||
|
client.sendStateEvent(roomId, eventType, eventContent1, stateKey1),
|
||||||
|
client.sendStateEvent(roomId, eventType, eventContent2, stateKey2),
|
||||||
|
client.sendStateEvent(roomId, eventType, eventContent3, stateKey3),
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.then((events) => {
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
// Read state events
|
||||||
|
sendActionFromIntegrationManager(
|
||||||
|
integrationManagerUrl,
|
||||||
|
roomId,
|
||||||
|
eventType,
|
||||||
|
true, // Any state key
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response")
|
||||||
|
.should("include.text", events[0].event_id)
|
||||||
|
.should("include.text", `"content":${JSON.stringify(eventContent1)}`)
|
||||||
|
.should("include.text", events[1].event_id)
|
||||||
|
.should("include.text", `"content":${JSON.stringify(eventContent2)}`)
|
||||||
|
.should("include.text", events[2].event_id)
|
||||||
|
.should("include.text", `"content":${JSON.stringify(eventContent3)}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail to read an event type which is not allowed", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
const eventType = "com.example.event";
|
||||||
|
const stateKey = "";
|
||||||
|
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
// Read state events
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response").should("include.text", "Failed to read events");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
261
cypress/e2e/integration-manager/send_event.spec.ts
Normal file
261
cypress/e2e/integration-manager/send_event.spec.ts
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { SynapseInstance } from "../../plugins/synapsedocker";
|
||||||
|
import { UserCredentials } from "../../support/login";
|
||||||
|
|
||||||
|
const ROOM_NAME = "Integration Manager Test";
|
||||||
|
const USER_DISPLAY_NAME = "Alice";
|
||||||
|
|
||||||
|
const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal";
|
||||||
|
const INTEGRATION_MANAGER_HTML = `
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Fake Integration Manager</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<input type="text" id="target-room-id"/>
|
||||||
|
<input type="text" id="event-type"/>
|
||||||
|
<input type="text" id="state-key"/>
|
||||||
|
<input type="text" id="event-content"/>
|
||||||
|
<button name="Send" id="send-action">Press to send action</button>
|
||||||
|
<button name="Close" id="close">Press to close</button>
|
||||||
|
<p id="message-response">No response</p>
|
||||||
|
<script>
|
||||||
|
document.getElementById("send-action").onclick = () => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
action: "send_event",
|
||||||
|
room_id: document.getElementById("target-room-id").value,
|
||||||
|
type: document.getElementById("event-type").value,
|
||||||
|
state_key: document.getElementById("state-key").value,
|
||||||
|
content: JSON.parse(document.getElementById("event-content").value),
|
||||||
|
},
|
||||||
|
'*',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
document.getElementById("close").onclick = () => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
action: "close_scalar",
|
||||||
|
},
|
||||||
|
'*',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Listen for a postmessage response
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
document.getElementById("message-response").innerText = JSON.stringify(event.data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
function openIntegrationManager() {
|
||||||
|
cy.get(".mx_RightPanel_roomSummaryButton").click();
|
||||||
|
cy.get(".mx_RoomSummaryCard_appsGroup").within(() => {
|
||||||
|
cy.contains("Add widgets, bridges & bots").click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendActionFromIntegrationManager(
|
||||||
|
integrationManagerUrl: string,
|
||||||
|
targetRoomId: string,
|
||||||
|
eventType: string,
|
||||||
|
stateKey: string,
|
||||||
|
content: Record<string, unknown>,
|
||||||
|
) {
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#target-room-id").should("exist").type(targetRoomId);
|
||||||
|
cy.get("#event-type").should("exist").type(eventType);
|
||||||
|
if (stateKey) {
|
||||||
|
cy.get("#state-key").should("exist").type(stateKey);
|
||||||
|
}
|
||||||
|
cy.get("#event-content").should("exist").type(JSON.stringify(content), { parseSpecialCharSequences: false });
|
||||||
|
cy.get("#send-action").should("exist").click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Integration Manager: Send Event", () => {
|
||||||
|
let testUser: UserCredentials;
|
||||||
|
let synapse: SynapseInstance;
|
||||||
|
let integrationManagerUrl: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.serveHtmlFile(INTEGRATION_MANAGER_HTML).then((url) => {
|
||||||
|
integrationManagerUrl = url;
|
||||||
|
});
|
||||||
|
cy.startSynapse("default").then((data) => {
|
||||||
|
synapse = data;
|
||||||
|
|
||||||
|
cy.initTestUser(synapse, USER_DISPLAY_NAME, () => {
|
||||||
|
cy.window().then((win) => {
|
||||||
|
win.localStorage.setItem("mx_scalar_token", INTEGRATION_MANAGER_TOKEN);
|
||||||
|
win.localStorage.setItem(`mx_scalar_token_at_${integrationManagerUrl}`, INTEGRATION_MANAGER_TOKEN);
|
||||||
|
});
|
||||||
|
}).then((user) => {
|
||||||
|
testUser = user;
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.setAccountData("m.widgets", {
|
||||||
|
"m.integration_manager": {
|
||||||
|
content: {
|
||||||
|
type: "m.integration_manager",
|
||||||
|
name: "Integration Manager",
|
||||||
|
url: integrationManagerUrl,
|
||||||
|
data: {
|
||||||
|
api_url: integrationManagerUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "integration-manager",
|
||||||
|
},
|
||||||
|
}).as("integrationManager");
|
||||||
|
|
||||||
|
// Succeed when checking the token is valid
|
||||||
|
cy.intercept(`${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, (req) => {
|
||||||
|
req.continue((res) => {
|
||||||
|
return res.send(200, {
|
||||||
|
user_id: testUser.userId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.createRoom({
|
||||||
|
name: ROOM_NAME,
|
||||||
|
}).as("roomId");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cy.stopSynapse(synapse);
|
||||||
|
cy.stopWebServers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send a state event", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
const eventContent = {
|
||||||
|
foo: "bar",
|
||||||
|
};
|
||||||
|
const stateKey = "state-key-123";
|
||||||
|
|
||||||
|
// Send the event
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response").should("include.text", "event_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the event
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||||
|
})
|
||||||
|
.then((event) => {
|
||||||
|
expect(event).to.deep.equal(eventContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send a state event with empty content", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
const eventContent = {};
|
||||||
|
const stateKey = "state-key-123";
|
||||||
|
|
||||||
|
// Send the event
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response").should("include.text", "event_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the event
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||||
|
})
|
||||||
|
.then((event) => {
|
||||||
|
expect(event).to.be.empty;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send a state event with empty state key", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
const eventType = "io.element.integrations.installations";
|
||||||
|
const eventContent = {
|
||||||
|
foo: "bar",
|
||||||
|
};
|
||||||
|
const stateKey = "";
|
||||||
|
|
||||||
|
// Send the event
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response").should("include.text", "event_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the event
|
||||||
|
cy.getClient()
|
||||||
|
.then(async (client) => {
|
||||||
|
return await client.getStateEvent(roomId, eventType, stateKey);
|
||||||
|
})
|
||||||
|
.then((event) => {
|
||||||
|
expect(event).to.deep.equal(eventContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail to send an event type which is not allowed", () => {
|
||||||
|
cy.all([cy.get<string>("@roomId"), cy.get<{}>("@integrationManager")]).then(([roomId]) => {
|
||||||
|
cy.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
openIntegrationManager();
|
||||||
|
|
||||||
|
const eventType = "com.example.event";
|
||||||
|
const eventContent = {
|
||||||
|
foo: "bar",
|
||||||
|
};
|
||||||
|
const stateKey = "";
|
||||||
|
|
||||||
|
// Send the event
|
||||||
|
sendActionFromIntegrationManager(integrationManagerUrl, roomId, eventType, stateKey, eventContent);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
cy.accessIframe(`iframe[src*="${integrationManagerUrl}"]`).within(() => {
|
||||||
|
cy.get("#message-response").should("include.text", "Failed to send event");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -264,10 +264,36 @@ Get an openID token for the current user session.
|
||||||
Request: No parameters
|
Request: No parameters
|
||||||
Response:
|
Response:
|
||||||
- The openId token object as described in https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridopenidrequest_token
|
- The openId token object as described in https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridopenidrequest_token
|
||||||
|
|
||||||
|
send_event
|
||||||
|
----------
|
||||||
|
Sends an event in a room.
|
||||||
|
|
||||||
|
Request:
|
||||||
|
- type is the event type to send.
|
||||||
|
- state_key is the state key to send. Omitted if not a state event.
|
||||||
|
- content is the event content to send.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
- room_id is the room ID where the event was sent.
|
||||||
|
- event_id is the event ID of the event which was sent.
|
||||||
|
|
||||||
|
read_events
|
||||||
|
-----------
|
||||||
|
Read events from a room.
|
||||||
|
|
||||||
|
Request:
|
||||||
|
- type is the event type to read.
|
||||||
|
- state_key is the state key to read, or `true` to read all events of the type. Omitted if not a state event.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
- events: Array of events. If none found, this will be an empty array.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { IEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
|
@ -295,6 +321,8 @@ enum Action {
|
||||||
SetBotOptions = "set_bot_options",
|
SetBotOptions = "set_bot_options",
|
||||||
SetBotPower = "set_bot_power",
|
SetBotPower = "set_bot_power",
|
||||||
GetOpenIdToken = "get_open_id_token",
|
GetOpenIdToken = "get_open_id_token",
|
||||||
|
SendEvent = "send_event",
|
||||||
|
ReadEvents = "read_events",
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendResponse(event: MessageEvent<any>, res: any): void {
|
function sendResponse(event: MessageEvent<any>, res: any): void {
|
||||||
|
@ -468,13 +496,13 @@ function setWidget(event: MessageEvent<any>, roomId: string | null): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWidgets(event: MessageEvent<any>, roomId: string): void {
|
function getWidgets(event: MessageEvent<any>, roomId: string | null): void {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
if (!client) {
|
if (!client) {
|
||||||
sendError(event, _t("You need to be logged in."));
|
sendError(event, _t("You need to be logged in."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let widgetStateEvents = [];
|
let widgetStateEvents: Partial<IEvent>[] = [];
|
||||||
|
|
||||||
if (roomId) {
|
if (roomId) {
|
||||||
const room = client.getRoom(roomId);
|
const room = client.getRoom(roomId);
|
||||||
|
@ -693,6 +721,141 @@ async function getOpenIdToken(event: MessageEvent<any>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendEvent(
|
||||||
|
event: MessageEvent<{
|
||||||
|
type: string;
|
||||||
|
state_key?: string;
|
||||||
|
content?: IContent;
|
||||||
|
}>,
|
||||||
|
roomId: string,
|
||||||
|
) {
|
||||||
|
const eventType = event.data.type;
|
||||||
|
const stateKey = event.data.state_key;
|
||||||
|
const content = event.data.content;
|
||||||
|
|
||||||
|
if (typeof eventType !== "string") {
|
||||||
|
sendError(event, _t("Failed to send event"), new Error("Invalid 'type' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const allowedEventTypes = ["m.widgets", "im.vector.modular.widgets", "io.element.integrations.installations"];
|
||||||
|
if (!allowedEventTypes.includes(eventType)) {
|
||||||
|
sendError(event, _t("Failed to send event"), new Error("Disallowed 'type' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content || typeof content !== "object") {
|
||||||
|
sendError(event, _t("Failed to send event"), new Error("Invalid 'content' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
if (!client) {
|
||||||
|
sendError(event, _t("You need to be logged in."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = client.getRoom(roomId);
|
||||||
|
if (!room) {
|
||||||
|
sendError(event, _t("This room is not recognised."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateKey !== undefined) {
|
||||||
|
// state event
|
||||||
|
try {
|
||||||
|
const res = await client.sendStateEvent(roomId, eventType, content, stateKey);
|
||||||
|
sendResponse(event, {
|
||||||
|
room_id: roomId,
|
||||||
|
event_id: res.event_id,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
sendError(event, _t("Failed to send event"), e as Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// message event
|
||||||
|
sendError(event, _t("Failed to send event"), new Error("Sending message events is not implemented"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readEvents(
|
||||||
|
event: MessageEvent<{
|
||||||
|
type: string;
|
||||||
|
state_key?: string | boolean;
|
||||||
|
limit?: number;
|
||||||
|
}>,
|
||||||
|
roomId: string,
|
||||||
|
) {
|
||||||
|
const eventType = event.data.type;
|
||||||
|
const stateKey = event.data.state_key;
|
||||||
|
const limit = event.data.limit;
|
||||||
|
|
||||||
|
if (typeof eventType !== "string") {
|
||||||
|
sendError(event, _t("Failed to read events"), new Error("Invalid 'type' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const allowedEventTypes = [
|
||||||
|
"m.room.power_levels",
|
||||||
|
"m.room.encryption",
|
||||||
|
"m.room.member",
|
||||||
|
"m.room.name",
|
||||||
|
"m.widgets",
|
||||||
|
"im.vector.modular.widgets",
|
||||||
|
"io.element.integrations.installations",
|
||||||
|
];
|
||||||
|
if (!allowedEventTypes.includes(eventType)) {
|
||||||
|
sendError(event, _t("Failed to read events"), new Error("Disallowed 'type' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let effectiveLimit: number;
|
||||||
|
if (limit !== undefined) {
|
||||||
|
if (typeof limit !== "number" || limit < 0) {
|
||||||
|
sendError(event, _t("Failed to read events"), new Error("Invalid 'limit' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
effectiveLimit = Math.min(limit, Number.MAX_SAFE_INTEGER);
|
||||||
|
} else {
|
||||||
|
effectiveLimit = Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
if (!client) {
|
||||||
|
sendError(event, _t("You need to be logged in."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = client.getRoom(roomId);
|
||||||
|
if (!room) {
|
||||||
|
sendError(event, _t("This room is not recognised."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateKey !== undefined) {
|
||||||
|
// state events
|
||||||
|
if (typeof stateKey !== "string" && stateKey !== true) {
|
||||||
|
sendError(event, _t("Failed to read events"), new Error("Invalid 'state_key' in request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// When `true` is passed for state key, get events with any state key.
|
||||||
|
const effectiveStateKey = stateKey === true ? undefined : stateKey;
|
||||||
|
|
||||||
|
let events: MatrixEvent[] = [];
|
||||||
|
events = events.concat(room.currentState.getStateEvents(eventType, effectiveStateKey as string) || []);
|
||||||
|
events = events.slice(0, effectiveLimit);
|
||||||
|
|
||||||
|
sendResponse(event, {
|
||||||
|
events: events.map((e) => e.getEffectiveEvent()),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// message events
|
||||||
|
sendError(event, _t("Failed to read events"), new Error("Reading message events is not implemented"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onMessage = function (event: MessageEvent<any>): void {
|
const onMessage = function (event: MessageEvent<any>): void {
|
||||||
if (!event.origin) {
|
if (!event.origin) {
|
||||||
// stupid chrome
|
// stupid chrome
|
||||||
|
@ -786,6 +949,12 @@ const onMessage = function (event: MessageEvent<any>): void {
|
||||||
} else if (event.data.action === Action.CanSendEvent) {
|
} else if (event.data.action === Action.CanSendEvent) {
|
||||||
canSendEvent(event, roomId);
|
canSendEvent(event, roomId);
|
||||||
return;
|
return;
|
||||||
|
} else if (event.data.action === Action.SendEvent) {
|
||||||
|
sendEvent(event, roomId);
|
||||||
|
return;
|
||||||
|
} else if (event.data.action === Action.ReadEvents) {
|
||||||
|
readEvents(event, roomId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
|
|
|
@ -390,6 +390,8 @@
|
||||||
"Power level must be positive integer.": "Power level must be positive integer.",
|
"Power level must be positive integer.": "Power level must be positive integer.",
|
||||||
"You are not in this room.": "You are not in this room.",
|
"You are not in this room.": "You are not in this room.",
|
||||||
"You do not have permission to do that in this room.": "You do not have permission to do that in this room.",
|
"You do not have permission to do that in this room.": "You do not have permission to do that in this room.",
|
||||||
|
"Failed to send event": "Failed to send event",
|
||||||
|
"Failed to read events": "Failed to read events",
|
||||||
"Missing room_id in request": "Missing room_id in request",
|
"Missing room_id in request": "Missing room_id in request",
|
||||||
"Room %(roomId)s not visible": "Room %(roomId)s not visible",
|
"Room %(roomId)s not visible": "Room %(roomId)s not visible",
|
||||||
"Missing user_id in request": "Missing user_id in request",
|
"Missing user_id in request": "Missing user_id in request",
|
||||||
|
|
Loading…
Reference in a new issue