Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
0412c35
init new github
garrrikkotua Oct 31, 2024
5705571
types and grid
garrrikkotua Oct 31, 2024
040b95c
client
garrrikkotua Oct 31, 2024
d96883f
move snowflake to lib
garrrikkotua Nov 5, 2024
9c7b4b0
stuff
garrrikkotua Nov 5, 2024
7200b42
integration
garrrikkotua Nov 6, 2024
164240a
progress
garrrikkotua Nov 7, 2024
c9fbc20
Cleanup github webhooks routes
Nov 12, 2024
a5d30d4
remove webhooks logic
garrrikkotua Nov 13, 2024
41d3353
remove some types
garrrikkotua Nov 13, 2024
a5b8aae
Merge branches 'feat/github-archive' and 'main' of github.com:CrowdDo…
gaspergrom Nov 13, 2024
2d59559
types and stuff
garrrikkotua Nov 14, 2024
2377e64
progress
garrrikkotua Nov 14, 2024
d2371e7
Github archive integration frontend
gaspergrom Nov 14, 2024
62d222d
Merge branch 'feat/github-archive' of github.com:CrowdDotDev/crowd.de…
gaspergrom Nov 14, 2024
edb5c80
issues and stuff
garrrikkotua Nov 15, 2024
aedbb35
stuff
garrrikkotua Nov 18, 2024
84ce248
Add org list and design changes
gaspergrom Nov 18, 2024
dffbfac
Merge branch 'feat/github-archive' of github.com:CrowdDotDev/crowd.de…
gaspergrom Nov 18, 2024
bdfd13a
stuff
garrrikkotua Nov 18, 2024
a74fad9
Github archive integration finish and connect
gaspergrom Nov 18, 2024
f18dd04
Merge branch 'feat/github-archive' of github.com:CrowdDotDev/crowd.de…
gaspergrom Nov 18, 2024
b11fc25
Strip trailing spaces
Nov 18, 2024
ddafb39
Make a way to create snowflake client shortly
Nov 18, 2024
45eae0f
Create endpoints for new github onboarding flow
Nov 18, 2024
189c093
Github mapping fix
gaspergrom Nov 18, 2024
c0b1616
Merge branch 'feat/github-archive' of github.com:CrowdDotDev/crowd.de…
gaspergrom Nov 18, 2024
3b1276e
Fetch mappings
gaspergrom Nov 18, 2024
8ed8aac
remove locs from commits
garrrikkotua Nov 19, 2024
7ec4541
kinda working
garrrikkotua Nov 19, 2024
6639a40
kinda working
garrrikkotua Nov 19, 2024
6541967
stuff
garrrikkotua Nov 20, 2024
c1a7b09
associate commits with PRs
garrrikkotua Nov 25, 2024
66d482a
Create migration for github integration settings format from old to new
Nov 26, 2024
408c155
fix ilp
garrrikkotua Nov 26, 2024
2d4e896
dont throw error on reopened
garrrikkotua Nov 26, 2024
bf609e8
logic when we update
garrrikkotua Nov 26, 2024
4b36d45
clean up a little bit
garrrikkotua Nov 26, 2024
c5272f7
remove endpoint
garrrikkotua Nov 27, 2024
9c55d68
remove sf test
garrrikkotua Nov 27, 2024
416218b
remove some more missing repos
garrrikkotua Nov 27, 2024
b380ae4
some changes
garrrikkotua Nov 28, 2024
6c00277
fix messageSentAt
garrrikkotua Nov 28, 2024
a26afcf
fix
garrrikkotua Nov 28, 2024
fde1f73
fix tokens
garrrikkotua Nov 28, 2024
f920447
increase threshold
garrrikkotua Nov 28, 2024
c3c45dd
Use lower and upper limits correctly when querying activities
Nov 29, 2024
9acea9d
Populate github attributes with attributes from matching git activity
Nov 28, 2024
a8d814c
Exchange attributes only for authored-commit activities
Nov 29, 2024
244da57
Include channel in finding matching git/github activity
Nov 29, 2024
3889452
handle repo deletions
garrrikkotua Dec 3, 2024
18c305a
remapping
garrrikkotua Dec 3, 2024
cceceda
ignore deleted mapping
garrrikkotua Dec 3, 2024
0851772
logging
garrrikkotua Dec 3, 2024
ce902ae
delete mappings on frontend
garrrikkotua Dec 3, 2024
323ab88
overwrite deleted at
garrrikkotua Dec 3, 2024
06a7850
disable settings updates when repo is in progress
garrrikkotua Dec 3, 2024
bcf88d1
fix snowlake idle connections
garrrikkotua Dec 3, 2024
acbcc05
refresh integrations after deletions
garrrikkotua Dec 3, 2024
bc722de
add tooltip
garrrikkotua Dec 3, 2024
ddbdc1f
fix conversation starter
garrrikkotua Dec 4, 2024
d1bb5b6
refresh repo settings
garrrikkotua Dec 4, 2024
ce32d7d
repo_id and fix conversations
garrrikkotua Dec 5, 2024
7e84fcd
fix init client
garrrikkotua Dec 5, 2024
da1ef1b
Add some logging to progress wrapper
joanagmaia Dec 5, 2024
7e878b2
Use useTimeoutPoll to fetch progress endpoint
joanagmaia Dec 5, 2024
f03246f
change segments the right way
garrrikkotua Dec 6, 2024
6b5e8c9
don't use in-progress state when updating
garrrikkotua Dec 6, 2024
fbc9961
Merge branch 'main' into feat/github-archive
garrrikkotua Dec 9, 2024
6e81949
some formating
garrrikkotua Dec 9, 2024
1e333c1
bring back webhooks
garrrikkotua Dec 9, 2024
48b4bdb
fix import
garrrikkotua Dec 9, 2024
ee0b036
fix things for webhooks
garrrikkotua Dec 9, 2024
e343209
fix eslint
garrrikkotua Dec 10, 2024
2eb7db7
Merge branch 'main' into feat/github-archive
garrrikkotua Dec 10, 2024
999b266
remove unneeded envs
garrrikkotua Dec 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,13 @@
"clientSecret": "CROWD_GITLAB_CLIENT_SECRET",
"callbackUrl": "CROWD_GITLAB_CALLBACK_URL",
"webhookToken": "CROWD_GITLAB_WEBHOOK_TOKEN"
},
"snowflake": {
"privateKey": "CROWD_SNOWFLAKE_PRIVATE_KEY",
"account": "CROWD_SNOWFLAKE_ACCOUNT",
"username": "CROWD_SNOWFLAKE_USERNAME",
"database": "CROWD_SNOWFLAKE_DATABASE",
"warehouse": "CROWD_SNOWFLAKE_WAREHOUSE",
"role": "CROWD_SNOWFLAKE_ROLE"
}
}
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"@crowd/temporal": "workspace:*",
"@crowd/tracing": "workspace:*",
"@crowd/types": "workspace:*",
"@crowd/snowflake": "workspace:*",
"@google-cloud/storage": "5.3.0",
"@octokit/auth-app": "^3.6.1",
"@octokit/core": "^6.1.2",
Expand Down
10 changes: 10 additions & 0 deletions backend/src/api/integration/helpers/githubOrgRepos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Permissions from '@/security/permissions'
import GithubIntegrationService from '@/services/githubIntegrationService'
import PermissionChecker from '@/services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.integrationEdit)

const payload = await GithubIntegrationService.getOrgRepos(req.params.org)
await req.responseHandler.success(req, res, payload)
}
10 changes: 10 additions & 0 deletions backend/src/api/integration/helpers/githubSearchOrgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Permissions from '@/security/permissions'
import GithubIntegrationService from '@/services/githubIntegrationService'
import PermissionChecker from '@/services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.integrationEdit)

const payload = await GithubIntegrationService.findOrgs(req.query.query)
await req.responseHandler.success(req, res, payload)
}
10 changes: 10 additions & 0 deletions backend/src/api/integration/helpers/githubSearchRepos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Permissions from '@/security/permissions'
import GithubIntegrationService from '@/services/githubIntegrationService'
import PermissionChecker from '@/services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.integrationEdit)

const payload = await GithubIntegrationService.findGithubRepos(req.query.query)
await req.responseHandler.success(req, res, payload)
}
12 changes: 12 additions & 0 deletions backend/src/api/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ export default (app) => {
`/tenant/:tenantId/integration/:id/github/repos`,
safeWrap(require('./helpers/githubMapReposGet').default),
)
app.get(
`/tenant/:tenantId/integration/github/search/orgs`,
safeWrap(require('./helpers/githubSearchOrgs').default),
)
app.get(
`/tenant/:tenantId/integration/github/search/repos`,
safeWrap(require('./helpers/githubSearchRepos').default),
)
app.get(
`/tenant/:tenantId/integration/github/orgs/:org/repos`,
safeWrap(require('./helpers/githubOrgRepos').default),
)
app.put(
`/discord-authenticate/:tenantId/:guild_id`,
safeWrap(require('./helpers/discordAuthenticate').default),
Expand Down
9 changes: 9 additions & 0 deletions backend/src/conf/configTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,12 @@ export interface IRedditConfig {
clientId: string
clientSecret: string
}

export interface SnowflakeConfiguration {
privateKey: string
account: string
username: string
database: string
warehouse: string
role: string
}
5 changes: 5 additions & 0 deletions backend/src/conf/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
TwitterConfiguration,
UnleashConfiguration,
WeeklyEmailsConfiguration,
SnowflakeConfiguration,
} from './configTypes'

// TODO-kube
Expand Down Expand Up @@ -172,3 +173,7 @@ export const OPEN_STATUS_API_CONFIG: IOpenStatusApiConfig =
export const GITLAB_CONFIG: GitlabConfiguration = config.get<GitlabConfiguration>('gitlab')

export const REDDIT_CONFIG: IRedditConfig = config.get<IRedditConfig>('reddit')


export const SNOWFLAKE_CONFIG: SnowflakeConfiguration =
config.get<SnowflakeConfiguration>('snowflake')
63 changes: 63 additions & 0 deletions backend/src/services/githubIntegrationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { request } from '@octokit/request'

import { GithubSnowflakeClient, SnowflakeClient } from '@crowd/snowflake'

import { IServiceOptions } from './IServiceOptions'

export default class GithubIntegrationService {
constructor(private readonly options: IServiceOptions) {}

public async getGithubRepositories(org: string) {
const client = SnowflakeClient.fromEnv()
this.options.log.info(`Getting GitHub repositories for org: ${org}`)
const githubClient = new GithubSnowflakeClient(client)
return githubClient.getOrgRepositories({ org, perPage: 10000 })
}
Comment on lines +12 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize Snowflake client initialization and add error handling

  1. The Snowflake client is created for each method call, which could be inefficient. Consider initializing it in the constructor.
  2. The high page limit (10000) might cause performance issues with large organizations.
  3. Missing error handling for Snowflake operations.
export default class GithubIntegrationService {
+ private readonly snowflakeClient: SnowflakeClient;
+ private readonly githubSnowflakeClient: GithubSnowflakeClient;

- constructor(private readonly options: IServiceOptions) {}
+ constructor(private readonly options: IServiceOptions) {
+   this.snowflakeClient = SnowflakeClient.fromEnv();
+   this.githubSnowflakeClient = new GithubSnowflakeClient(this.snowflakeClient);
+ }

  public async getGithubRepositories(org: string) {
-   const client = SnowflakeClient.fromEnv()
    this.options.log.info(`Getting GitHub repositories for org: ${org}`)
-   const githubClient = new GithubSnowflakeClient(client)
-   return githubClient.getOrgRepositories({ org, perPage: 10000 })
+   try {
+     return await this.githubSnowflakeClient.getOrgRepositories({ 
+       org, 
+       perPage: 1000 // Consider implementing pagination
+     });
+   } catch (error) {
+     this.options.log.error(`Failed to get GitHub repositories for org: ${org}`, error);
+     throw new GitHubIntegrationError(`Failed to get repositories for ${org}`, error);
+   }
  }

Committable suggestion skipped: line range outside the PR's diff.


public static async findGithubRepos(query: string) {
const [orgRepos, repos] = await Promise.all([
request('GET /search/repositories', {
q: `owner:${query}`,
}).catch((err) => {
this.options.log.error(`Error getting GitHub repositories for org: ${query}`, err)
return { data: { items: [] } }
}),
request('GET /search/repositories', {
q: query,
}),
])

// console.log([...orgRepos.data.items, ...repos.data.items])

return [...orgRepos.data.items, ...repos.data.items].map((item) => ({
name: item.name,
url: item.html_url,
org: {
name: item.owner.login,
url: item.owner.html_url,
logo: item.owner.avatar_url,
},
}))
}
Comment on lines +18 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling and reduce code duplication

  1. Current error handling silently fails by returning empty arrays
  2. No rate limiting consideration for GitHub API
  3. Duplicate request configuration code
+ private createGitHubRequest(endpoint: string, params: object) {
+   return request(endpoint, {
+     ...params,
+     headers: {
+       authorization: `bearer ${GITHUB_TOKEN_CONFIG.token}`,
+     },
+   });
+ }

  public async findGithubRepos(query: string) {
-   const auth = GITHUB_TOKEN_CONFIG.token
+   try {
      const [orgRepos, repos] = await Promise.all([
-       request('GET /search/repositories', {
-         q: `owner:${query}`,
-         headers: {
-           authorization: `bearer ${auth}`,
-         }
-       }).catch((err) => {
-         this.options.log.error(`Error getting GitHub repositories for org: ${query}`, err)
-         return { data: { items: [] } }
-       }),
+       this.createGitHubRequest('GET /search/repositories', { q: `owner:${query}` }),
+       this.createGitHubRequest('GET /search/repositories', { q: query })
-       request('GET /search/repositories', {
-         q: query,
-         headers: {
-           authorization: `bearer ${auth}`,
-         }
-       }).catch((err) => {
-         this.options.log.error(`Error getting GitHub repositories for org: ${query}`, err)
-         return { data: { items: [] } }
-       }),
      ])

      return [...orgRepos.data.items, ...repos.data.items].map((item) => ({
        name: item.name,
        url: item.html_url,
        org: {
          name: item.owner.login,
          url: item.owner.html_url,
          logo: item.owner.avatar_url,
        },
      }))
+   } catch (error) {
+     this.options.log.error(`Failed to search GitHub repositories for query: ${query}`, error);
+     throw new GitHubIntegrationError(`Failed to search repositories for ${query}`, error);
+   }
  }

Committable suggestion skipped: line range outside the PR's diff.


public static async findOrgs(query: string) {
const response = await request('GET /search/users', {
q: query,
})
return response.data.items.map((item) => ({
name: item.login,
url: item.html_url,
logo: item.avatar_url,
}))
}
Comment on lines +53 to +67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Convert static method to instance method and add error handling

  1. Static methods can't use the class's logging functionality
  2. Missing error handling
  3. Inconsistent with other instance methods
- public static async findOrgs(query: string) {
+ public async findOrgs(query: string) {
-   const auth = GITHUB_TOKEN_CONFIG.token
+   try {
-     const response = await request('GET /search/users', {
-       q: query,
-       headers: {
-         authorization: `bearer ${auth}`,
-       }
-     })
+     const response = await this.createGitHubRequest('GET /search/users', { q: query });
      return response.data.items.map((item) => ({
        name: item.login,
        url: item.html_url,
        logo: item.avatar_url,
      }))
+   } catch (error) {
+     this.options.log.error(`Failed to search GitHub organizations for query: ${query}`, error);
+     throw new GitHubIntegrationError(`Failed to search organizations for ${query}`, error);
+   }
  }

Committable suggestion skipped: line range outside the PR's diff.


public static async getOrgRepos(org: string) {
const response = await request('GET /orgs/{org}/repos', {
org,
})
return response.data.map((repo) => ({
name: repo.name,
url: repo.html_url,
}))
}
Comment on lines +68 to +81

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor static method and resolve potential duplication

  1. This method has similar functionality to getGithubRepositories
  2. Being static prevents using class's logging
  3. Missing error handling

Consider consolidating this with getGithubRepositories or clearly document the difference in purpose:

- public static async getOrgRepos(org: string) {
+ public async getOrgRepos(org: string) {
+   // TODO: Document why this method exists alongside getGithubRepositories
+   try {
-     const auth = GITHUB_TOKEN_CONFIG.token
-     const response = await request('GET /orgs/{org}/repos', {
-       org,
-       headers: {
-         authorization: `bearer ${auth}`,
-       }
-     })
+     const response = await this.createGitHubRequest('GET /orgs/{org}/repos', { org });
      return response.data.map((repo) => ({
        name: repo.name,
        url: repo.html_url,
      }))
+   } catch (error) {
+     this.options.log.error(`Failed to get repositories for organization: ${org}`, error);
+     throw new GitHubIntegrationError(`Failed to get repositories for ${org}`, error);
+   }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static async getOrgRepos(org: string) {
const auth = GITHUB_TOKEN_CONFIG.token
const response = await request('GET /orgs/{org}/repos', {
org,
headers: {
authorization: `bearer ${auth}`,
}
})
return response.data.map((repo) => ({
name: repo.name,
url: repo.html_url,
}))
}
public async getOrgRepos(org: string) {
// TODO: Document why this method exists alongside getGithubRepositories
try {
const response = await this.createGitHubRequest('GET /orgs/{org}/repos', { org });
return response.data.map((repo) => ({
name: repo.name,
url: repo.html_url,
}))
} catch (error) {
this.options.log.error(`Failed to get repositories for organization: ${org}`, error);
throw new GitHubIntegrationError(`Failed to get repositories for ${org}`, error);
}
}

}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

Loading