Skip to content

Commit

Permalink
feat: home
Browse files Browse the repository at this point in the history
  • Loading branch information
christophehurpeau committed May 21, 2020
1 parent f116dfb commit dfc351c
Show file tree
Hide file tree
Showing 26 changed files with 761 additions and 223 deletions.
280 changes: 221 additions & 59 deletions dist/index-node10-dev.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-node10-dev.cjs.js.map

Large diffs are not rendered by default.

280 changes: 221 additions & 59 deletions dist/index-node10.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-node10.cjs.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -64,7 +64,7 @@
"dependencies": {
"@commitlint/parse": "8.3.4",
"@octokit/webhooks": "6.3.2",
"@slack/web-api": "5.7.0",
"@slack/web-api": "5.9.0",
"body-parser": "1.19.0",
"cookie-parser": "1.4.5",
"dotenv": "8.2.0",
Expand Down
5 changes: 4 additions & 1 deletion src/app/org-settings.tsx
Expand Up @@ -37,7 +37,10 @@ export default function orgSettings(
const org = orgs.data.find((o) => o.login === req.params.org);
if (!org) return res.redirect('/app/gh');

await syncOrg(mongoStores, user.api, org);
const o = await mongoStores.orgs.findByKey(org.id);
if (!o) return res.redirect('/app/gh');

await syncOrg(mongoStores, user.api, o.installationId as number, org);
await syncTeams(mongoStores, user.api, org);

res.redirect(`/app/gh/org/${req.params.org}`);
Expand Down
88 changes: 71 additions & 17 deletions src/context/initTeamSlack.ts
@@ -1,10 +1,12 @@
import Webhooks from '@octokit/webhooks';
import { WebClient, KnownBlock } from '@slack/web-api';
import { Context, Octokit } from 'probot';
import { MongoStores } from '../mongo';
import { createLink } from '../slack/utils';
import { MongoStores, Org } from '../mongo';
import { getUserDmSettings } from '../dm/getUserDmSettings';
import { MessageCategory } from '../dm/MessageCategory';
import { Config } from '../orgsConfigs';
import * as slackHome from '../slack/home';
import { getKeys } from './utils';

interface SlackMessage {
Expand All @@ -31,24 +33,26 @@ export interface TeamSlack {
pr: Octokit.PullsGetResponse,
context: Context<T>,
) => string;
updateHome: (githubLogin: string) => void;
}

export const voidTeamSlack = (): TeamSlack => ({
mention: (): string => '',
link: (): string => '',
postMessage: (): Promise<null> => Promise.resolve(null),
prLink: (): string => '',
updateHome: (): void => undefined,
});

export const initTeamSlack = async <GroupNames extends string>(
mongoStores: MongoStores,
context: Context<any>,
config: Config<GroupNames>,
slackToken?: string,
org: Org,
): Promise<TeamSlack> => {
const owner = context.payload.repository.owner;

if (!slackToken) {
if (!org.slackToken) {
return voidTeamSlack();
}

Expand All @@ -60,22 +64,59 @@ export const initTeamSlack = async <GroupNames extends string>(
}, {});

const slackEmails = Object.values(githubLoginToSlackEmail);
const slackClient = new WebClient(slackToken);
const slackClient = new WebClient(org.slackToken);

const membersInDb = await mongoStores.orgMembers.findAll({
'org.id': org._id,
});

const members: [string, { member: any; im: any }][] = [];
const foundEmailMembers: string[] = [];

await slackClient.paginate('users.list', {}, (page: any) => {
page.members.forEach((member: any) => {
const email = member.profile && member.profile.email;
if (email && slackEmails.includes(email)) {
members.push([email, { member, im: undefined }]);
}
});
return false;
Object.entries(githubLoginToSlackEmail).forEach(([login, email]) => {
const member = membersInDb.find((m) => m.user.login === login);
if (member?.slack?.id) {
foundEmailMembers.push(email);
members.push([email, { member: { id: member.slack.id }, im: undefined }]);
}
});

if (foundEmailMembers.length !== slackEmails.length) {
const missingEmails = slackEmails.filter(
(email) => !foundEmailMembers.includes(email),
);

const memberEmailToMemberId = new Map<string, number>(
Object.entries(githubLoginToSlackEmail).map(([login, email]) => [
email,
membersInDb.find((m) => m.user.login === login)?._id as any,
]),
);

await slackClient.paginate('users.list', {}, (page: any) => {
page.members.forEach((member: any) => {
const email = member.profile && member.profile.email;
if (email && missingEmails.includes(email)) {
members.push([email, { member, im: undefined }]);
if (memberEmailToMemberId.has(email)) {
mongoStores.orgMembers.partialUpdateMany(
{
_id: memberEmailToMemberId.get(email),
},
{ $set: { slack: { id: member.id } } },
);
}
}
});
return false;
});
}

for (const [, user] of members) {
try {
const im: any = await slackClient.im.open({ user: user.member.id });
const im: any = await slackClient.conversations.open({
users: user.member.id,
});
user.im = im.channel;
} catch (err) {
console.error(err);
Expand Down Expand Up @@ -129,14 +170,27 @@ export const initTeamSlack = async <GroupNames extends string>(
if (!result.ok) return null;
return { ts: result.ts as string };
},
link: (url: string, text: string): string => {
return `<${url}|${text}>`;
},
link: createLink,
prLink: <T extends { repository: Webhooks.PayloadRepository }>(
pr: Octokit.PullsGetResponse,
context: Context<T>,
): string => {
return `<${pr.html_url}|${context.payload.repository.name}#${pr.number}>`;
return createLink(
pr.html_url,
`${context.payload.repository.name}#${pr.number}`,
);
},

updateHome: (githubLogin: string): void => {
context.log.debug('update slack home', { githubLogin });
const user = getUserFromGithubLogin(githubLogin);
if (!user || !user.member) return;

slackHome.updateMember(mongoStores, context.github, slackClient, {
user: { id: null, login: githubLogin },
org: { id: org._id, login: org.login },
slack: { id: user.member.id },
} as any);
},
};
};
15 changes: 8 additions & 7 deletions src/context/orgContext.ts
Expand Up @@ -33,11 +33,12 @@ export interface OrgContext<
const getOrCreateOrg = async (
mongoStores: MongoStores,
github: Octokit,
installationId: number,
orgInfo: { id: number; login: string },
): Promise<Org> => {
let org = await mongoStores.orgs.findByKey(orgInfo.id);
if (org) return org;
org = await syncOrg(mongoStores, github, orgInfo);
if (org?.installationId) return org;
org = await syncOrg(mongoStores, github, installationId, orgInfo);
await syncTeams(mongoStores, github, orgInfo);
return org;
};
Expand All @@ -48,13 +49,13 @@ const initTeamContext = async (
config: Config,
orgInfo: { id: number; login: string },
): Promise<OrgContext> => {
const org = await getOrCreateOrg(mongoStores, context.github, orgInfo);
const slackPromise = initTeamSlack(
const org = await getOrCreateOrg(
mongoStores,
context,
config,
org.slackToken,
context.github,
context.payload.installation.id,
orgInfo,
);
const slackPromise = initTeamSlack(mongoStores, context, config, org);

const githubLoginToGroup = new Map<string, string>();
getKeys(config.groups).forEach((groupName) => {
Expand Down
2 changes: 1 addition & 1 deletion src/context/repoContext.ts
Expand Up @@ -128,7 +128,7 @@ async function initRepoContext<GroupNames extends string>(
// eslint-disable-next-line @typescript-eslint/no-misused-promises
lock(prIdOrIds, async (createReleaseCallback) => {
const release = createReleaseCallback(() => {});
context.log.info('lock: lock acquired', logInfos);
context.log.info('lock: lock pr acquired', logInfos);
try {
await callback();
} catch (err) {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Expand Up @@ -3,6 +3,7 @@ import { Probot, Application } from 'probot';
import mongoInit from './mongo';
import appRouter from './appRouter';
import initApp from './initApp';
import { updateAllOrgs } from './slack/home';

if (!process.env.REVIEWFLOW_NAME) process.env.REVIEWFLOW_NAME = 'reviewflow';
console.log({ name: process.env.REVIEWFLOW_NAME });
Expand All @@ -20,4 +21,5 @@ Probot.run((app: Application): void => {
const mongoStores = mongoInit();
appRouter(app, mongoStores);
initApp(app, mongoStores);
updateAllOrgs(mongoStores, (id: number) => app.auth(id));
});
1 change: 1 addition & 0 deletions src/initApp.ts
Expand Up @@ -32,6 +32,7 @@ export default function initApp(
await syncOrg(
mongoStores,
context.github,
orgContext.org.installationId as number,
context.payload.organization,
);
},
Expand Down
8 changes: 5 additions & 3 deletions src/mongo.ts
Expand Up @@ -24,12 +24,14 @@ export interface User extends MongoModel {
// TODO _id is number
export interface Org extends MongoModel {
login: string;
installationId?: number;
slackToken?: string;
}

export interface OrgMembers extends MongoModel {
export interface OrgMember extends MongoModel {
org: { id: number; login: string };
user: { id: number; login: string };
slack?: { id: string };
}

export interface OrgTeam extends MongoModel {
Expand All @@ -44,7 +46,7 @@ export interface MongoStores {
userDmSettings: MongoStore<UserDmSettings>;
users: MongoStore<User>;
orgs: MongoStore<Org>;
orgMembers: MongoStore<OrgMembers>;
orgMembers: MongoStore<OrgMember>;
orgTeams: MongoStore<OrgTeam>;
// prEvents: MongoStore<PrEventsModel>;
}
Expand Down Expand Up @@ -88,7 +90,7 @@ export default function init(): MongoStores {
coll.createIndex({ login: 1 }, { unique: true });
});

const orgMembers = new MongoStore<OrgMembers>(connection, 'orgMembers');
const orgMembers = new MongoStore<OrgMember>(connection, 'orgMembers');
orgMembers.collection.then((coll) => {
coll.createIndex({ 'user.id': 1, 'org.id': 1 }, { unique: true });
});
Expand Down
72 changes: 42 additions & 30 deletions src/org-handlers/actions/syncOrg.ts
@@ -1,50 +1,62 @@
import { Octokit } from 'probot';
import { MongoStores, Org } from '../../mongo';

interface OrgInfo {
login: string;
id: number;
}

export const syncOrg = async (
mongoStores: MongoStores,
github: Octokit,
org: { login: string; id: number },
installationId: number,
org: OrgInfo,
): Promise<Org> => {
const orgInStore = await mongoStores.orgs.upsertOne({
_id: org.id as any, // TODO _id is number
login: org.login,
installationId,
});

const orgEmbed = { id: org.id, login: org.login };

const memberIds: number[] = [];

await github.paginate(
github.orgs.listMembers.endpoint.merge({
org: org.login,
}),
async (
{ data }: Octokit.Response<Octokit.OrgsListMembersResponse>,
done,
) => {
await Promise.all(
data.map((member) => {
memberIds.push(member.id);
return Promise.all([
mongoStores.orgMembers.upsertOne({
_id: `${org.id}_${member.id}`,
org: orgEmbed,
user: {
id: member.id,
await Promise.all(
await github.paginate(
github.orgs.listMembers.endpoint.merge({
org: org.login,
}),
({ data }: Octokit.Response<Octokit.OrgsListMembersResponse>) => {
return Promise.all(
data.map(async (member) => {
memberIds.push(member.id);
return Promise.all([
(await mongoStores.orgMembers.collection).updateOne(
{ _id: `${org.id}_${member.id}` },
{
$set: {
org: orgEmbed,
user: {
id: member.id,
login: member.login,
},
},
$setOnInsert: {
created: new Date(),
},
},
),
mongoStores.users.upsertOne({
_id: member.id as any,
login: member.login,
},
}),
mongoStores.users.upsertOne({
_id: member.id as any,
login: member.login,
type: member.type,
}),
]);
}),
);
done();
},
type: member.type,
}),
]);
}),
);
},
),
);

await mongoStores.orgMembers.deleteMany({
Expand Down

0 comments on commit dfc351c

Please sign in to comment.