From 5bb4bf74f5749426857c481e0249ab7ddb14deab Mon Sep 17 00:00:00 2001 From: Joan Reyero Date: Mon, 27 Nov 2023 18:11:39 +0100 Subject: [PATCH] Feature/with feature flags (#1716) Co-authored-by: Joana Maia --- .gitignore | 4 +- backend/.env.dist.local | 5 +- .../config/custom-environment-variables.json | 3 + backend/config/default.json | 1 + backend/package-lock.json | 39 ++-- backend/src/api/member/index.ts | 5 + backend/src/api/member/memberFindGithub.ts | 26 +++ backend/src/bin/scripts/unleash-init.ts | 3 +- backend/src/conf/configTypes.ts | 4 + backend/src/conf/index.ts | 4 + backend/src/services/memberService.ts | 24 +++ .../components/list/member-list-table.vue | 8 + .../components/member-dropdown-content.vue | 34 +++- .../member/components/member-dropdown.vue | 4 +- .../member/components/member-enrichment.vue | 60 ++++-- .../components/member-find-github-drawer.vue | 175 ++++++++++++++++++ .../components/view/member-view-header.vue | 7 + frontend/src/modules/member/member-service.js | 10 + frontend/src/utils/featureFlag/index.js | 1 + services/libs/common/src/i18n/en.ts | 3 + services/libs/types/src/enums/featureFlags.ts | 1 + 21 files changed, 383 insertions(+), 38 deletions(-) create mode 100644 backend/src/api/member/memberFindGithub.ts create mode 100644 frontend/src/modules/member/components/member-find-github-drawer.vue diff --git a/.gitignore b/.gitignore index b8034b7a91..fc6c23bc76 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,6 @@ api-test/__pycache__/api_test.cpython-39-pytest-6.2.4.pyc /backend/superface/** docker/volume -services/libs/*/dist \ No newline at end of file +services/libs/*/dist + +**/.cubestore \ No newline at end of file diff --git a/backend/.env.dist.local b/backend/.env.dist.local index 37d5f12aca..7d9a3815a2 100755 --- a/backend/.env.dist.local +++ b/backend/.env.dist.local @@ -167,6 +167,9 @@ CROWD_ANALYTICS_TENANT_ID= CROWD_ANALYTICS_BASE_URL= CROWD_ANALYTICS_API_TOKEN= +# GITHUB_TOKEN +CROWD_GITHUB_TOKEN= + # Temporal CROWD_TEMPORAL_SERVER_URL=localhost:7233 CROWD_TEMPORAL_NAMESPACE=default @@ -174,4 +177,4 @@ CROWD_TEMPORAL_ENCRYPTION_KEY_ID=local CROWD_TEMPORAL_ENCRYPTION_KEY=FweBMRnGCLshER8FlSvNusQA6G3MRUKt # Seach sync api -CROWD_SEARCH_SYNC_API_URL=http://search-sync-api:8083 \ No newline at end of file +CROWD_SEARCH_SYNC_API_URL=http://search-sync-api:8083 diff --git a/backend/config/custom-environment-variables.json b/backend/config/custom-environment-variables.json index 54b7dc639b..b4d5b67f27 100644 --- a/backend/config/custom-environment-variables.json +++ b/backend/config/custom-environment-variables.json @@ -158,6 +158,9 @@ "url": "CROWD_EAGLE_EYE_URL", "apiKey": "CROWD_EAGLE_EYE_API_KEY" }, + "githubToken": { + "token": "CROWD_GITHUB_TOKEN" + }, "slackAlerting": { "url": "CROWD_SLACK_ALERTING_URL" }, diff --git a/backend/config/default.json b/backend/config/default.json index f738e966fa..5d9bdd4737 100644 --- a/backend/config/default.json +++ b/backend/config/default.json @@ -41,6 +41,7 @@ "enrichment": {}, "organizationEnrichment": {}, "eagleEye": {}, + "githubToken": {}, "unleash": { "db": {} }, diff --git a/backend/package-lock.json b/backend/package-lock.json index ce2c6edbb9..674cfa2890 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1604,7 +1604,7 @@ "dependencies": { "@crowd/logging": "file:../logging", "@crowd/types": "file:../types", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "psl": "^1.9.0", "uuid": "^9.0.0" }, @@ -3145,12 +3145,15 @@ } }, "../services/libs/cubejs": { + "name": "@crowd/cubejs", "version": "1.0.0", "dependencies": { + "@crowd/common": "file:../common", "@crowd/logging": "file:../logging", "@cubejs-backend/server-core": "^0.32.27", "@cubejs-client/core": "~0.34.9", - "jsonwebtoken": "~9.0.2" + "jsonwebtoken": "~9.0.2", + "moment": "~2.29.4" }, "devDependencies": { "@types/node": "^18.16.3", @@ -37896,7 +37899,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -40668,6 +40671,7 @@ "@crowd/cubejs": { "version": "file:../services/libs/cubejs", "requires": { + "@crowd/common": "file:../common", "@crowd/logging": "file:../logging", "@cubejs-backend/server-core": "^0.32.27", "@cubejs-client/core": "~0.34.9", @@ -40678,6 +40682,7 @@ "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", "jsonwebtoken": "~9.0.2", + "moment": "~2.29.4", "prettier": "^2.8.8", "typescript": "^5.0.4" } @@ -40737,7 +40742,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -43536,7 +43541,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -48587,7 +48592,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -51383,7 +51388,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -54182,7 +54187,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -57915,7 +57920,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -60693,7 +60698,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -63492,7 +63497,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -69908,7 +69913,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -72707,7 +72712,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -77970,7 +77975,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -80769,7 +80774,7 @@ "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", - "lodash": "~4.17.21", + "lodash.get": "~4.4.2", "prettier": "^2.8.8", "psl": "^1.9.0", "typescript": "^5.0.4", @@ -88196,7 +88201,9 @@ }, "ajv-formats": { "version": "2.1.1", - "requires": {} + "requires": { + "ajv": "^8.0.0" + } }, "amp": { "version": "0.3.1" diff --git a/backend/src/api/member/index.ts b/backend/src/api/member/index.ts index 97faa31950..7142e65172 100644 --- a/backend/src/api/member/index.ts +++ b/backend/src/api/member/index.ts @@ -26,6 +26,11 @@ export default (app) => { app.get(`/tenant/:tenantId/member`, safeWrap(require('./memberList').default)) app.get(`/tenant/:tenantId/member/active`, safeWrap(require('./memberActiveList').default)) app.get(`/tenant/:tenantId/member/:id`, safeWrap(require('./memberFind').default)) + app.get( + `/tenant/:tenantId/member/github/:id`, + featureFlagMiddleware(FeatureFlag.FIND_GITHUB, 'errors.featureFlag.notEnabled'), + safeWrap(require('./memberFindGithub').default), + ) app.put(`/tenant/:tenantId/member/:memberId/merge`, safeWrap(require('./memberMerge').default)) app.put( `/tenant/:tenantId/member/:memberId/no-merge`, diff --git a/backend/src/api/member/memberFindGithub.ts b/backend/src/api/member/memberFindGithub.ts new file mode 100644 index 0000000000..80a272cc00 --- /dev/null +++ b/backend/src/api/member/memberFindGithub.ts @@ -0,0 +1,26 @@ +import Permissions from '../../security/permissions' +import MemberService from '../../services/memberService' +import PermissionChecker from '../../services/user/permissionChecker' + +/** + * GET /tenant/{tenantId}/member/{id} + * @summary Find a member + * @tag Members + * @security Bearer + * @description Find a single member by ID. + * @pathParam {string} tenantId - Your workspace/tenant ID + * @pathParam {string} id - The ID of the member + * @response 200 - Ok + * @responseContent {MemberResponse} 200.application/json + * @responseExample {MemberFind} 200.application/json.Member + * @response 401 - Unauthorized + * @response 404 - Not found + * @response 429 - Too many requests + */ +export default async (req, res) => { + new PermissionChecker(req).validateHas(Permissions.values.memberRead) + + const payload = await new MemberService(req).findGithub(req.params.id) + + await req.responseHandler.success(req, res, payload) +} diff --git a/backend/src/bin/scripts/unleash-init.ts b/backend/src/bin/scripts/unleash-init.ts index 597150b6af..49351a63a9 100644 --- a/backend/src/bin/scripts/unleash-init.ts +++ b/backend/src/bin/scripts/unleash-init.ts @@ -129,6 +129,7 @@ const constaintConfiguration = { }, ], ], + [FeatureFlag.FIND_GITHUB]: [[]], [FeatureFlag.LINKEDIN]: [ [ { @@ -428,7 +429,7 @@ async function createStrategy(flag: FeatureFlag, constraints: any[]): Promise('eagleEye') +export const GITHUB_TOKEN_CONFIG: GithubTokenConfiguration = + config.get('githubToken') + export const UNLEASH_CONFIG: UnleashConfiguration = config.get('unleash') export const OPENSEARCH_CONFIG: IOpenSearchConfig = config.get('opensearch') diff --git a/backend/src/services/memberService.ts b/backend/src/services/memberService.ts index 83c6ca3206..b7622442ea 100644 --- a/backend/src/services/memberService.ts +++ b/backend/src/services/memberService.ts @@ -41,6 +41,7 @@ import MemberAttributeSettingsService from './memberAttributeSettingsService' import OrganizationService from './organizationService' import SearchSyncService from './searchSyncService' import SettingsService from './settingsService' +import { GITHUB_TOKEN_CONFIG } from '../conf' import { ServiceType } from '@/conf/configTypes' export default class MemberService extends LoggerBase { @@ -789,6 +790,29 @@ export default class MemberService extends LoggerBase { }) } + async findGithub(memberId) { + const memberIdentities = (await MemberRepository.findById(memberId, this.options)).username + const axios = require('axios') + // GitHub allows a maximum of 5 parameters + const identities = Object.values(memberIdentities).flat().slice(0, 5) + // Join the usernames for search + const identitiesQuery = identities.join('+OR+') + const url = `https://api.github.com/search/users?q=${identitiesQuery}` + const headers = { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${GITHUB_TOKEN_CONFIG.token}`, + 'X-GitHub-Api-Version': '2022-11-28', + } + const response = await axios.get(url, { headers }) + const data = response.data.items.map((item) => ({ + username: item.login, + avatarUrl: item.avatar_url, + score: item.score, + url: item.html_url, + })) + return data + } + /** * Given two members, add them to the toMerge fields of each other. * It will also update the tenant's toMerge list, removing any entry that contains diff --git a/frontend/src/modules/member/components/list/member-list-table.vue b/frontend/src/modules/member/components/list/member-list-table.vue index 1c0309ac05..1c710e2a6c 100644 --- a/frontend/src/modules/member/components/list/member-list-table.vue +++ b/frontend/src/modules/member/components/list/member-list-table.vue @@ -650,11 +650,16 @@ + @@ -680,6 +685,7 @@ import { getSegmentsFromProjectGroup } from '@/utils/segments'; import AppMemberMergeDialog from '@/modules/member/components/member-merge-dialog.vue'; import AppTagPopover from '@/modules/tag/components/tag-popover.vue'; import AppPagination from '@/shared/pagination/pagination.vue'; +import AppMemberFindGithubDrawer from '@/modules/member/components/member-find-github-drawer.vue'; import AppSharedTagList from '@/shared/tag/tag-list.vue'; import AppSvg from '@/shared/svg/svg.vue'; import AppMemberBadge from '../member-badge.vue'; @@ -708,6 +714,8 @@ const memberDropdownPopover = ref(null); const actionBtnRefs = ref({}); const selectedActionMember = ref(null); +const isFindGithubDrawerOpen = ref(null); + const props = defineProps({ hasIntegrations: { type: Boolean, diff --git a/frontend/src/modules/member/components/member-dropdown-content.vue b/frontend/src/modules/member/components/member-dropdown-content.vue index d81ff13d14..01a3515136 100644 --- a/frontend/src/modules/member/components/member-dropdown-content.vue +++ b/frontend/src/modules/member/components/member-dropdown-content.vue @@ -52,6 +52,22 @@ +