mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Add support for deleting connections
This commit is contained in:
parent
9140c4d1bc
commit
95e23473b2
@ -435,8 +435,8 @@ export class Bridge {
|
|||||||
);
|
);
|
||||||
connManager.push(discussionConnection);
|
connManager.push(discussionConnection);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.error(ex);
|
log.error("Failed to create discussion room", ex);
|
||||||
throw Error('Failed to create discussion room');
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,11 +351,12 @@ export class ConnectionManager {
|
|||||||
return this.connections.filter((c) => (c instanceof GitLabRepoConnection && c.path === pathWithNamespace)) as GitLabRepoConnection[];
|
return this.connections.filter((c) => (c instanceof GitLabRepoConnection && c.path === pathWithNamespace)) as GitLabRepoConnection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionsForJiraProject(project: JiraProject, eventName: string): JiraProjectConnection[] {
|
public getConnectionsForJiraProject(project: {id: string, self: string, key: string}, eventName?: string): JiraProjectConnection[] {
|
||||||
return this.connections.filter((c) =>
|
return this.connections.filter((c) =>
|
||||||
(c instanceof JiraProjectConnection &&
|
(c instanceof JiraProjectConnection &&
|
||||||
c.interestedInProject(project) &&
|
c.interestedInProject(project) &&
|
||||||
c.isInterestedInHookEvent(eventName))) as JiraProjectConnection[];
|
(!eventName || c.isInterestedInHookEvent(eventName))
|
||||||
|
)) as JiraProjectConnection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -388,12 +389,12 @@ export class ConnectionManager {
|
|||||||
return this.connections.find((c) => c.connectionId === connectionId && c.roomId === roomId);
|
return this.connections.find((c) => c.connectionId === connectionId && c.roomId === roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async purgeConnection(roomId: string, connectionId: string, requireNoRemoveHandler = true) {
|
public async purgeConnection(roomId: string, connectionId: string, requireRemoveHandler = true) {
|
||||||
const connection = this.connections.find((c) => c.connectionId === connectionId && c.roomId == roomId);
|
const connection = this.connections.find((c) => c.connectionId === connectionId && c.roomId == roomId);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
throw Error("Connection not found");
|
throw Error("Connection not found");
|
||||||
}
|
}
|
||||||
if (requireNoRemoveHandler && !connection.onRemove) {
|
if (requireRemoveHandler && !connection.onRemove) {
|
||||||
throw Error("Connection doesn't support removal, and so cannot be safely removed");
|
throw Error("Connection doesn't support removal, and so cannot be safely removed");
|
||||||
}
|
}
|
||||||
await connection.onRemove?.();
|
await connection.onRemove?.();
|
||||||
|
@ -487,7 +487,13 @@ export class GitHubRepoConnection extends CommandConnection implements IConnecti
|
|||||||
}
|
}
|
||||||
const orgRepoName = event.repository.full_name;
|
const orgRepoName = event.repository.full_name;
|
||||||
|
|
||||||
let message = `**${event.issue.user.login}** created new issue [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${event.issue.title}"`;
|
let actionText = "created new issue"
|
||||||
|
if (event.issue.comments > 0) {
|
||||||
|
// This was tranferred
|
||||||
|
actionText = "transferred issue"
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = `**${event.issue.user.login}** ${actionText} [${orgRepoName}#${event.issue.number}](${event.issue.html_url}): "${event.issue.title}"`;
|
||||||
message += (event.issue.assignee ? ` assigned to ${event.issue.assignee.login}` : '');
|
message += (event.issue.assignee ? ` assigned to ${event.issue.assignee.login}` : '');
|
||||||
if (this.showIssueRoomLink) {
|
if (this.showIssueRoomLink) {
|
||||||
const appInstance = await this.githubInstance.getSafeOctokitForRepo(this.org, this.repo);
|
const appInstance = await this.githubInstance.getSafeOctokitForRepo(this.org, this.repo);
|
||||||
|
@ -109,7 +109,7 @@ export class JiraProjectConnection extends CommandConnection implements IConnect
|
|||||||
return !this.state.events || this.state.events?.includes(eventName as JiraAllowedEventsNames);
|
return !this.state.events || this.state.events?.includes(eventName as JiraAllowedEventsNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interestedInProject(project: JiraProject) {
|
public interestedInProject(project: {id: string, self: string, key: string}) {
|
||||||
if (this.projectId === project.id) {
|
if (this.projectId === project.id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,11 @@ import markdown from "markdown-it";
|
|||||||
import { FigmaFileConnection } from "./FigmaFileConnection";
|
import { FigmaFileConnection } from "./FigmaFileConnection";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import { AdminRoom } from "../AdminRoom";
|
import { AdminRoom } from "../AdminRoom";
|
||||||
|
import { ConnectionManager } from "../ConnectionManager";
|
||||||
const md = new markdown();
|
const md = new markdown();
|
||||||
|
|
||||||
|
const GITHUB_REGEX = /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/;
|
||||||
|
const JIRA_REGEX = /.+\/projects\/(\w+)\/?(\w+\/?)*$/;
|
||||||
/**
|
/**
|
||||||
* Handles setting up a room with connections. This connection is "virtual" in that it has
|
* Handles setting up a room with connections. This connection is "virtual" in that it has
|
||||||
* no state, and is only invoked when messages from other clients fall through.
|
* no state, and is only invoked when messages from other clients fall through.
|
||||||
@ -28,6 +31,7 @@ export class SetupConnection extends CommandConnection {
|
|||||||
private readonly as: Appservice,
|
private readonly as: Appservice,
|
||||||
private readonly tokenStore: UserTokenStore,
|
private readonly tokenStore: UserTokenStore,
|
||||||
private readonly config: BridgeConfig,
|
private readonly config: BridgeConfig,
|
||||||
|
private readonly connectionManager: ConnectionManager,
|
||||||
private readonly getOrCreateAdminRoom: (userId: string) => Promise<AdminRoom>,
|
private readonly getOrCreateAdminRoom: (userId: string) => Promise<AdminRoom>,
|
||||||
private readonly githubInstance?: GithubInstance,) {
|
private readonly githubInstance?: GithubInstance,) {
|
||||||
super(
|
super(
|
||||||
@ -59,7 +63,7 @@ export class SetupConnection extends CommandConnection {
|
|||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
throw new CommandError("User not logged in", "You are not logged into GitHub. Start a DM with this bot and use the command `github login`.");
|
throw new CommandError("User not logged in", "You are not logged into GitHub. Start a DM with this bot and use the command `github login`.");
|
||||||
}
|
}
|
||||||
const urlParts = /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(url.trim().toLowerCase());
|
const urlParts = GITHUB_REGEX.exec(url.trim().toLowerCase());
|
||||||
if (!urlParts) {
|
if (!urlParts) {
|
||||||
throw new CommandError("Invalid GitHub url", "The GitHub url you entered was not valid");
|
throw new CommandError("Invalid GitHub url", "The GitHub url you entered was not valid");
|
||||||
}
|
}
|
||||||
@ -69,6 +73,38 @@ export class SetupConnection extends CommandConnection {
|
|||||||
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge ${org}/${repo}`);
|
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge ${org}/${repo}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@botCommand("remove github repo", "Remove a GitHub repository connection.", ["url"], [], true)
|
||||||
|
public async onRemoveGitHubRepo(userId: string, url: string) {
|
||||||
|
if (!this.githubInstance || !this.config.github) {
|
||||||
|
throw new CommandError("not-configured", "The bridge is not configured to support GitHub");
|
||||||
|
}
|
||||||
|
if (!this.config.checkPermission(userId, "github", BridgePermissionLevel.manageConnections)) {
|
||||||
|
throw new CommandError('You are not permitted to remove connections for GitHub');
|
||||||
|
}
|
||||||
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to remove integrations.");
|
||||||
|
}
|
||||||
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to remove a bridge in this room. Please promote me to an Admin/Moderator");
|
||||||
|
}
|
||||||
|
const urlParts = GITHUB_REGEX.exec(url.trim().toLowerCase());
|
||||||
|
if (!urlParts) {
|
||||||
|
throw new CommandError("Invalid GitHub url", "The GitHub url you entered was not valid");
|
||||||
|
}
|
||||||
|
const [, org, repo] = urlParts;
|
||||||
|
const connections = await this.connectionManager.getConnectionsForGithubRepo(org, repo);
|
||||||
|
for (const connection of connections) {
|
||||||
|
await this.connectionManager.purgeConnection(connection.roomId, connection.connectionId, false);
|
||||||
|
}
|
||||||
|
if (connections.length === 0) {
|
||||||
|
throw new CommandError("no-connections-found", "No connections found matching that url");
|
||||||
|
} else if (connections.length === 1) {
|
||||||
|
await this.as.botClient.sendNotice(this.roomId, `Removed connection from this room`);
|
||||||
|
} else {
|
||||||
|
await this.as.botClient.sendNotice(this.roomId, `Removed ${connections.length} connections from this room`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@botCommand("jira project", "Create a connection for a JIRA project. (You must be logged in with JIRA to do this)", ["url"], [], true)
|
@botCommand("jira project", "Create a connection for a JIRA project. (You must be logged in with JIRA to do this)", ["url"], [], true)
|
||||||
public async onJiraProject(userId: string, urlStr: string) {
|
public async onJiraProject(userId: string, urlStr: string) {
|
||||||
const url = new URL(urlStr);
|
const url = new URL(urlStr);
|
||||||
@ -88,7 +124,7 @@ export class SetupConnection extends CommandConnection {
|
|||||||
if (!jiraClient) {
|
if (!jiraClient) {
|
||||||
throw new CommandError("User not logged in", "You are not logged into Jira. Start a DM with this bot and use the command `jira login`.");
|
throw new CommandError("User not logged in", "You are not logged into Jira. Start a DM with this bot and use the command `jira login`.");
|
||||||
}
|
}
|
||||||
const urlParts = /.+\/projects\/(\w+)\/?(\w+\/?)*$/.exec(url.pathname.toLowerCase());
|
const urlParts = JIRA_REGEX.exec(url.pathname.toLowerCase());
|
||||||
const projectKey = urlParts?.[1] || url.searchParams.get('projectKey');
|
const projectKey = urlParts?.[1] || url.searchParams.get('projectKey');
|
||||||
if (!projectKey) {
|
if (!projectKey) {
|
||||||
throw new CommandError("Invalid Jira url", "The JIRA project url you entered was not valid. It should be in the format of `https://jira-instance/.../projects/PROJECTKEY/...` or `.../RapidBoard.jspa?projectKey=TEST`");
|
throw new CommandError("Invalid Jira url", "The JIRA project url you entered was not valid. It should be in the format of `https://jira-instance/.../projects/PROJECTKEY/...` or `.../RapidBoard.jspa?projectKey=TEST`");
|
||||||
@ -99,6 +135,44 @@ export class SetupConnection extends CommandConnection {
|
|||||||
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge Jira project ${res.connection.projectKey}`);
|
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge Jira project ${res.connection.projectKey}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@botCommand("remove jira project", "Remove a GitHub repository connection.", ["url"], [], true)
|
||||||
|
public async onRemoveJiraProject(userId: string, urlStr: string) {
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
if (!this.config.jira) {
|
||||||
|
throw new CommandError("not-configured", "The bridge is not configured to support Jira");
|
||||||
|
}
|
||||||
|
if (!this.config.checkPermission(userId, "jira", BridgePermissionLevel.manageConnections)) {
|
||||||
|
throw new CommandError('You are not permitted to remove connections for Jira');
|
||||||
|
}
|
||||||
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to remove connections.");
|
||||||
|
}
|
||||||
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to remove a connection in this room. Please promote me to an Admin/Moderator");
|
||||||
|
}
|
||||||
|
const urlParts = JIRA_REGEX.exec(url.pathname.toLowerCase());
|
||||||
|
const projectKey = urlParts?.[1] || url.searchParams.get('projectKey');
|
||||||
|
if (!projectKey) {
|
||||||
|
throw new CommandError("Invalid Jira url", "The JIRA project url you entered was not valid. It should be in the format of `https://jira-instance/.../projects/PROJECTKEY/...` or `.../RapidBoard.jspa?projectKey=TEST`");
|
||||||
|
}
|
||||||
|
const safeUrl = `https://${url.host}/projects/${projectKey}`;
|
||||||
|
const connections = await this.connectionManager.getConnectionsForJiraProject({
|
||||||
|
id: "none",
|
||||||
|
self: safeUrl,
|
||||||
|
key: projectKey,
|
||||||
|
});
|
||||||
|
for (const connection of connections) {
|
||||||
|
await this.connectionManager.purgeConnection(connection.roomId, connection.connectionId, false);
|
||||||
|
}
|
||||||
|
if (connections.length === 0) {
|
||||||
|
throw new CommandError("no-connections-found", "No connections found matching that url");
|
||||||
|
} else if (connections.length === 1) {
|
||||||
|
await this.as.botClient.sendNotice(this.roomId, `Removed connection from this room`);
|
||||||
|
} else {
|
||||||
|
await this.as.botClient.sendNotice(this.roomId, `Removed ${connections.length} connections from this room`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@botCommand("webhook", "Create an inbound webhook", ["name"], [], true)
|
@botCommand("webhook", "Create an inbound webhook", ["name"], [], true)
|
||||||
public async onWebhook(userId: string, name: string) {
|
public async onWebhook(userId: string, name: string) {
|
||||||
if (!this.config.generic?.enabled) {
|
if (!this.config.generic?.enabled) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user