-
Notifications
You must be signed in to change notification settings - Fork 752
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'crowd-linux' into improvement/identity-optimizations
- Loading branch information
Showing
42 changed files
with
820 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { safeWrap } from '../../middlewares/errorMiddleware' | ||
|
||
export default (app) => { | ||
app.get(`/tenant/:tenantId/mergeActions`, safeWrap(require('./mergeActionQuery').default)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import MergeActionsService from '@/services/MergeActionsService' | ||
import Permissions from '../../security/permissions' | ||
import PermissionChecker from '../../services/user/permissionChecker' | ||
|
||
/** | ||
* GET /tenant/{tenantId}/mergeAction | ||
* @summary Query mergeActions | ||
* @tag MergeActions | ||
* @security Bearer | ||
* @description Query mergeActions. It accepts filters and pagination. | ||
* @pathParam {string} tenantId - Your workspace/tenant ID | ||
* @queryParam {string} entityId - ID of the entity | ||
* @queryParam {string} type - type of the entity (e.g., org or member) | ||
* @queryParam {number} [limit] - number of records to return (optional, default to 20) | ||
* @queryParam {number} [offset] - number of records to skip (optional, default to 0) | ||
* @response 200 - Ok | ||
* @responseContent {MergeActionList} 200.application/json | ||
* @responseExample {MergeActionList} 200.application/json.MergeAction | ||
* @response 401 - Unauthorized | ||
* @response 404 - Not found | ||
* @response 429 - Too many requests | ||
*/ | ||
export default async (req, res) => { | ||
new PermissionChecker(req).validateHas(Permissions.values.mergeActionRead) | ||
|
||
const payload = await new MergeActionsService(req).query(req.query) | ||
|
||
await req.responseHandler.success(req, res, payload) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/* eslint-disable @typescript-eslint/dot-notation */ | ||
/* eslint-disable no-console */ | ||
/* eslint-disable import/no-extraneous-dependencies */ | ||
|
||
import commandLineArgs from 'command-line-args' | ||
import commandLineUsage from 'command-line-usage' | ||
|
||
import { QueryExecutor } from '@crowd/data-access-layer/src/queryExecutor' | ||
import { databaseInit } from '@/database/databaseConnection' | ||
import SequelizeRepository from '@/database/repositories/sequelizeRepository' | ||
import { IRepositoryOptions } from '@/database/repositories/IRepositoryOptions' | ||
import OrganizationRepository from '@/database/repositories/organizationRepository' | ||
|
||
const options = [ | ||
{ | ||
name: 'help', | ||
alias: 'h', | ||
type: Boolean, | ||
description: 'Print this usage guide.', | ||
}, | ||
{ | ||
name: 'tenantId', | ||
alias: 't', | ||
type: String, | ||
description: 'Tenant Id', | ||
}, | ||
] | ||
const sections = [ | ||
{ | ||
header: `Fix empty displayName in organizations`, | ||
content: 'Script will fix organizations with empty displayName', | ||
}, | ||
{ | ||
header: 'Options', | ||
optionList: options, | ||
}, | ||
] | ||
|
||
const usage = commandLineUsage(sections) | ||
const parameters = commandLineArgs(options) | ||
|
||
function getOrgsWithoutDisplayName( | ||
qx: QueryExecutor, | ||
tenantId: string, | ||
{ limit = 50, countOnly = false }, | ||
) { | ||
return qx.select( | ||
` | ||
SELECT | ||
${countOnly ? 'COUNT(*)' : 'o.id'} | ||
FROM organizations o | ||
WHERE o."tenantId" = $(tenantId) | ||
AND o."displayName" IS NULL | ||
${countOnly ? '' : 'LIMIT $(limit)'} | ||
`, | ||
{ tenantId, limit }, | ||
) | ||
} | ||
|
||
async function getOrgIdentities(qx: QueryExecutor, orgId: string, tenantId: string) { | ||
return qx.select( | ||
` | ||
SELECT value | ||
FROM "organizationIdentities" | ||
WHERE "organizationId" = $(orgId) | ||
AND "tenantId" = $(tenantId) | ||
LIMIT 1 | ||
`, | ||
{ orgId, tenantId }, | ||
) | ||
} | ||
|
||
async function getOrgAttributes(qx: QueryExecutor, orgId: string) { | ||
return qx.select( | ||
` | ||
SELECT value | ||
FROM "orgAttributes" | ||
WHERE "organizationId" = $(orgId) | ||
AND name = 'name' | ||
LIMIT 1 | ||
`, | ||
{ orgId }, | ||
) | ||
} | ||
|
||
async function updateOrgDisplayName(qx: QueryExecutor, orgId: string, displayName: string) { | ||
await qx.result( | ||
` | ||
UPDATE organizations | ||
SET "displayName" = $(displayName) | ||
WHERE id = $(id) | ||
`, | ||
{ id: orgId, displayName }, | ||
) | ||
} | ||
|
||
if (parameters.help || !parameters.tenantId) { | ||
console.log(usage) | ||
} else { | ||
setImmediate(async () => { | ||
const prodDb = await databaseInit() | ||
const tenantId = parameters.tenantId | ||
const qx = SequelizeRepository.getQueryExecutor({ | ||
database: prodDb, | ||
} as IRepositoryOptions) | ||
|
||
const options = await SequelizeRepository.getDefaultIRepositoryOptions() | ||
|
||
const BATCH_SIZE = 50 | ||
let processed = 0 | ||
|
||
const totalOrgs = await getOrgsWithoutDisplayName(qx, tenantId, { countOnly: true }) | ||
|
||
console.log(`Total organizations without displayName: ${totalOrgs[0].count}`) | ||
|
||
let orgs = await getOrgsWithoutDisplayName(qx, tenantId, { limit: BATCH_SIZE }) | ||
|
||
while (totalOrgs[0].count > processed) { | ||
for (const org of orgs) { | ||
let displayName | ||
let updateAttributes = false | ||
|
||
const attributes = await getOrgAttributes(qx, org.id) | ||
|
||
if (attributes.length > 0) { | ||
displayName = attributes[0]?.value | ||
} else { | ||
const identities = await getOrgIdentities(qx, org.id, tenantId) | ||
displayName = identities && identities[0]?.value | ||
updateAttributes = true | ||
} | ||
|
||
if (displayName) { | ||
await updateOrgDisplayName(qx, org.id, displayName) | ||
|
||
if (updateAttributes) { | ||
await OrganizationRepository.updateOrgAttributes( | ||
org.id, | ||
{ | ||
attributes: { | ||
name: { | ||
custom: [displayName], | ||
default: displayName, | ||
}, | ||
}, | ||
}, | ||
options, | ||
) | ||
} | ||
} else { | ||
console.log(`Organization ${org.id} does not have displayName`) | ||
} | ||
|
||
processed++ | ||
} | ||
|
||
console.log(`Processed ${processed}/${totalOrgs[0].count} organizations`) | ||
|
||
orgs = await getOrgsWithoutDisplayName(qx, tenantId, { limit: BATCH_SIZE }) | ||
} | ||
|
||
process.exit(0) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
backend/src/database/migrations/U1721311363__addStepToMergeActions.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
alter table "mergeActions" drop column "step"; |
2 changes: 2 additions & 0 deletions
2
backend/src/database/migrations/V1721311363__addStepToMergeActions.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
alter table "mergeActions" add column "step" text; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { LoggerBase } from '@crowd/logging' | ||
import { queryMergeActions } from '@crowd/data-access-layer/src/mergeActions/repo' | ||
import { IServiceOptions } from './IServiceOptions' | ||
import SequelizeRepository from '@/database/repositories/sequelizeRepository' | ||
|
||
export default class MergeActionsService extends LoggerBase { | ||
options: IServiceOptions | ||
|
||
constructor(options: IServiceOptions) { | ||
super(options.log) | ||
this.options = options | ||
} | ||
|
||
async query(args) { | ||
const qx = SequelizeRepository.getQueryExecutor(this.options) | ||
const results = await queryMergeActions(qx, args) | ||
|
||
return results.map((result) => ({ | ||
primaryId: result.primaryId, | ||
secondaryId: result.secondaryId, | ||
state: result.state, | ||
// derive operation type from step and if step is null, default to merge | ||
operationType: result.step ? MergeActionsService.getOperationType(result.step) : 'merge', | ||
})) | ||
} | ||
|
||
static getOperationType(step) { | ||
if (step.startsWith('merge')) { | ||
return 'merge' | ||
} | ||
if (step.startsWith('unmerge')) { | ||
return 'unmerge' | ||
} | ||
|
||
throw new Error(`Unrecognized merge action step: ${step}`) | ||
} | ||
} |
Oops, something went wrong.