diff --git a/api/src/__tests__/initialize-loaders.test.js b/api/src/__tests__/initialize-loaders.test.js index c88c88be49..923f4ab5f7 100644 --- a/api/src/__tests__/initialize-loaders.test.js +++ b/api/src/__tests__/initialize-loaders.test.js @@ -27,9 +27,6 @@ describe('initializeLoaders', () => { 'loadDomainConnectionsByOrgId', 'loadDomainConnectionsByUserId', 'loadOrgByKey', - 'loadOrgBySlug', - 'loadOrgConnectionsByDomainId', - 'loadOrgConnectionsByUserId', 'loadUserByUserName', 'loadUserByKey', 'loadAffiliationByKey', diff --git a/api/src/create-context.js b/api/src/create-context.js index d58451fee5..5d95d7ebe9 100644 --- a/api/src/create-context.js +++ b/api/src/create-context.js @@ -13,7 +13,9 @@ import { WebScanDataSource } from './web-scan' import { AuditLogsDataSource } from './audit-logs' import { AdditionalFindingsDataSource } from './additional-findings' import { GuidanceTagDataSource } from './guidance-tag' +import { OrganizationDataSource } from './organization' import { TagsDataSource } from './tags' +import { DomainDataSource } from './domain' import { AuthDataSource, checkDomainOwnership, @@ -148,8 +150,28 @@ export async function createContext({ auditLogs: new AuditLogsDataSource({ query, userKey, cleanseInput, i18n, transaction, collections }), dnsScan: new DnsScanDataSource({ query, userKey, cleanseInput, i18n }), guidanceTag: new GuidanceTagDataSource({ query, userKey, i18n, language: request.language, cleanseInput }), + organization: new OrganizationDataSource({ + query, + userKey, + i18n, + language: request.language, + cleanseInput, + loginRequiredBool, + transaction, + collections, + }), tags: new TagsDataSource({ query, userKey, i18n, language: request.language, transaction, collections }), webScan: new WebScanDataSource({ query, userKey, cleanseInput, i18n }), + domain: new DomainDataSource({ + query, + userKey, + i18n, + language: request.language, + cleanseInput, + loginRequiredBool, + transaction, + collections, + }), }, loaders: initializeLoaders({ query, diff --git a/api/src/dmarc-summaries/objects/dmarc-summary.js b/api/src/dmarc-summaries/objects/dmarc-summary.js index 571fd1ccff..2da1e62316 100644 --- a/api/src/dmarc-summaries/objects/dmarc-summary.js +++ b/api/src/dmarc-summaries/objects/dmarc-summary.js @@ -20,9 +20,9 @@ export const dmarcSummaryType = new GraphQLObjectType({ resolve: async ( { domainKey }, _args, - { loaders: { loadDomainByKey } }, + { dataSources: { domain: domainDataSource } }, ) => { - const domain = await loadDomainByKey.load(domainKey) + const domain = await domainDataSource.byKey.load(domainKey) return domain }, }, diff --git a/api/src/domain/data-source.js b/api/src/domain/data-source.js new file mode 100644 index 0000000000..096fc211d8 --- /dev/null +++ b/api/src/domain/data-source.js @@ -0,0 +1,571 @@ +import { t } from '@lingui/macro' + +import { + loadDomainByDomain, + loadDomainByKey, + loadDomainConnectionsByOrgId, + loadDomainConnectionsByUserId, + loadDkimSelectorsByDomainId, +} from './loaders' + +export class DomainDataSource { + constructor({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, transaction, collections }) { + this._query = query + this._userKey = userKey + this._i18n = i18n + this._transaction = transaction + this._collections = collections + this.byDomain = loadDomainByDomain({ query, userKey, i18n }) + this.byKey = loadDomainByKey({ query, userKey, i18n }) + this.connectionsByOrgId = loadDomainConnectionsByOrgId({ + query, + userKey, + language, + cleanseInput, + i18n, + auth: { loginRequiredBool }, + }) + this.connectionsByUserId = loadDomainConnectionsByUserId({ + query, + userKey, + cleanseInput, + i18n, + auth: { loginRequiredBool }, + }) + this.dkimSelectorsByDomainId = loadDkimSelectorsByDomainId({ query, userKey, i18n }) + } + + async create({ insertDomain, org, tags, assetState }) { + const trx = await this._transaction(this._collections) + + let domainCursor + try { + domainCursor = await trx.step( + () => this._query` + UPSERT { domain: ${insertDomain.domain} } + INSERT ${insertDomain} + UPDATE { } + IN domains + RETURN NEW + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + let insertedDomain + try { + insertedDomain = await domainCursor.next() + } catch (err) { + console.error(`Cursor error occurred for user: ${this._userKey} when inserting new domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH claims + INSERT { + _from: ${org._id}, + _to: ${insertedDomain._id}, + tags: ${tags}, + assetState: ${assetState}, + firstSeen: ${new Date().toISOString()}, + } INTO claims + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was creating domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create domain. Please try again.`)) + } + + this.byDomain.clear(insertDomain.domain) + return this.byDomain.load(insertDomain.domain) + } + + async favourite({ domain, user }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH favourites + INSERT { + _from: ${user._id}, + _to: ${domain._id}, + } INTO favourites + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when inserting new domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was creating domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to favourite domain. Please try again.`)) + } + } + + async unfavourite({ domain, user }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH favourites, domains, users + LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} favourites RETURN { _key: e._key, _from: e._from, _to: e._to }) + LET edgeKeys = ( + FOR domainEdge IN domainEdges + FILTER domainEdge._to == ${domain._id} + FILTER domainEdge._from == ${user._id} + RETURN domainEdge._key + ) + FOR edgeKey IN edgeKeys + REMOVE edgeKey IN favourites + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction step error occurred for user: ${this._userKey} when removing domain edge: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to unfavourite domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was unfavouriting domain: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to unfavourite domain. Please try again.`)) + } + } + + async update({ domain, org, domainToInsert, claimToInsert }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + WITH domains + UPSERT { _key: ${domain._key} } + INSERT ${domainToInsert} + UPDATE ${domainToInsert} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain: ${domain._key}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + try { + await trx.step( + async () => + await this._query` + WITH claims + UPSERT { _from: ${org._id}, _to: ${domain._id} } + INSERT ${claimToInsert} + UPDATE ${claimToInsert} + IN claims + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain edge, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain edge. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: ${this._userKey} attempted to update domain: ${domain._key}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } + + async updateClaim({ claim, claimToInsert }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + WITH claims + UPSERT { _key: ${claim._key} } + INSERT ${claimToInsert} + UPDATE ${claimToInsert} + IN claims + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: ${this._userKey} attempted to update domain edge, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred while user: ${this._userKey} was updating domain claim: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to update domain. Please try again.`)) + } + } + + async remove({ domain, org, orgsClaimingDomain, hasOwnership }) { + const trx = await this._transaction(this._collections) + + if (hasOwnership) { + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries + LET dmarcSummaryEdges = ( + FOR v, e IN 1..1 OUTBOUND ${domain._id} domainsToDmarcSummaries + RETURN { edgeKey: e._key, dmarcSummaryId: e._to } + ) + LET removeDmarcSummaryEdges = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries + OPTIONS { waitForSync: true } + ) + LET removeDmarcSummary = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key + REMOVE key IN dmarcSummaries + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error( + `Trx step error occurred when removing dmarc summary data for user: ${this._userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains + LET domainEdges = ( + FOR v, e IN 1..1 INBOUND ${domain._id} ownership + REMOVE e._key IN ownership + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error( + `Trx step error occurred when removing ownership data for user: ${this._userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + if (orgsClaimingDomain <= 1) { + try { + await trx.step(async () => { + await this._query` + WITH web, webScan, domains + FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb + LET removeWebScansQuery = ( + FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans + REMOVE webScanV IN webScan + REMOVE webToWebScansV IN webToWebScans + OPTIONS { waitForSync: true } + ) + REMOVE webV IN web + REMOVE domainsWebEdge IN domainsWeb + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove web data for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH dns, domains + FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS + REMOVE dnsV IN dns + REMOVE domainsDNSEdge IN domainsDNS + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove DNS data for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH favourites, domains + FOR fav IN favourites + FILTER fav._to == ${domain._id} + REMOVE fav IN favourites + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove favourites for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR e IN domainsToSelectors + FILTER e._from == ${domain._id} + REMOVE e IN domainsToSelectors + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR claim IN claims + FILTER claim._to == ${domain._id} + REMOVE claim IN claims + REMOVE ${domain} IN domains + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove domain ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } else { + try { + await trx.step(async () => { + await this._query` + WITH claims, domains, organizations + LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} claims RETURN { _key: e._key, _from: e._from, _to: e._to }) + LET edgeKeys = ( + FOR domainEdge IN domainEdges + FILTER domainEdge._to == ${domain._id} + FILTER domainEdge._from == ${org._id} + RETURN domainEdge._key + ) + FOR edgeKey IN edgeKeys + REMOVE edgeKey IN claims + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error( + `Trx step error occurred while user: ${this._userKey} attempted to remove claim for ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Trx commit error occurred while user: ${this._userKey} attempted to remove ${domain.domain} in org: ${org.slug}, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove domain. Please try again.`)) + } + } + + async ignoreCve({ domain, ignoredCve, newIgnoredCves }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + UPSERT { _key: ${domain._key} } + INSERT ${{ ignoredCves: newIgnoredCves }} + UPDATE ${{ ignoredCves: newIgnoredCves }} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + let currentDomainVulnerabilitiesCursor + try { + currentDomainVulnerabilitiesCursor = await trx.step( + () => this._query` + FOR finding IN additionalFindings + FILTER finding.domain == ${domain._id} + LIMIT 1 + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN ${newIgnoredCves} + RETURN DISTINCT vuln.Cve + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}" when getting current CVEs, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}" when updating domain, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: "${this._userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to ignore CVE. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } + + async unignoreCve({ domain, ignoredCve, newIgnoredCves }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + async () => + await this._query` + UPSERT { _key: ${domain._key} } + INSERT ${{ ignoredCves: newIgnoredCves }} + UPDATE ${{ ignoredCves: newIgnoredCves }} + IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + let currentDomainVulnerabilitiesCursor + try { + currentDomainVulnerabilitiesCursor = await trx.step( + () => this._query` + FOR finding IN additionalFindings + FILTER finding.domain == ${domain._id} + LIMIT 1 + FOR wc IN finding.webComponents + FILTER LENGTH(wc.WebComponentCves) > 0 + FOR vuln IN wc.WebComponentCves + FILTER vuln.Cve NOT IN ${newIgnoredCves} + RETURN DISTINCT vuln.Cve + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}" when getting current CVEs, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains + `, + ) + } catch (err) { + console.error( + `Transaction step error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}" when updating domain, error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error( + `Transaction commit error occurred when user: "${this._userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domain._key}", error: ${err}`, + ) + await trx.abort() + throw new Error(this._i18n._(t`Unable to stop ignoring CVE. Please try again.`)) + } + + this.byKey.clear(domain._key) + return this.byKey.load(domain._key) + } +} diff --git a/api/src/domain/index.js b/api/src/domain/index.js index 0f0e03e958..fe73802636 100644 --- a/api/src/domain/index.js +++ b/api/src/domain/index.js @@ -2,3 +2,4 @@ export * from './loaders' export * from './mutations' export * from './queries' export * from './objects' +export * from './data-source' diff --git a/api/src/domain/mutations/__tests__/create-domain.test.js b/api/src/domain/mutations/__tests__/create-domain.test.js index 62e7cb9997..26c60b9e6b 100644 --- a/api/src/domain/mutations/__tests__/create-domain.test.js +++ b/api/src/domain/mutations/__tests__/create-domain.test.js @@ -21,6 +21,7 @@ import { } from '../../../auth' import { loadDkimSelectorsByDomainId, loadDomainByDomain } from '../../loaders' import { loadOrgByKey, loadOrgConnectionsByDomainId } from '../../../organization/loaders' +import { OrganizationDataSource } from '../../../organization/data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -209,6 +210,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ @@ -373,6 +384,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ @@ -513,6 +534,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ @@ -693,6 +724,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ @@ -836,6 +877,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ @@ -980,6 +1031,16 @@ describe('create a domain', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + loginRequiredBool: true, + transaction, + collections: collectionNames, + }), }, loaders: { loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ diff --git a/api/src/domain/mutations/add-organizations-domains.js b/api/src/domain/mutations/add-organizations-domains.js index 57d243ffc5..f80bc69007 100644 --- a/api/src/domain/mutations/add-organizations-domains.js +++ b/api/src/domain/mutations/add-organizations-domains.js @@ -54,7 +54,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ userKey, request: { ip }, auth: { checkPermission, saltedHash, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS }, validators: { cleanseInput }, }, ) => { @@ -98,7 +98,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to add domains to an organization: ${orgId} that does not exist.`) @@ -192,7 +192,7 @@ export const addOrganizationsDomains = new mutationWithClientMutationId({ } // Check to see if domain already exists in db - const checkDomain = await loadDomainByDomain.load(insertDomain.domain) + const checkDomain = await domainDS.byDomain.load(insertDomain.domain) // Setup Transaction const trx = await transaction(collections) diff --git a/api/src/domain/mutations/create-domain.js b/api/src/domain/mutations/create-domain.js index 4460f8f55e..cee1d168b8 100644 --- a/api/src/domain/mutations/create-domain.js +++ b/api/src/domain/mutations/create-domain.js @@ -4,7 +4,6 @@ import { t } from '@lingui/macro' import { createDomainUnion } from '../unions' import { Domain } from '../../scalars' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { AssetStateEnums } from '../../enums' import { headers } from 'nats' import { CvdEnrollmentInputOptions } from '../../additional-findings/input/cvd-enrollment-options' @@ -53,12 +52,10 @@ export const createDomain = new mutationWithClientMutationId({ i18n, request, query, - collections, - transaction, userKey, publish, auth: { checkPermission, saltedHash, userRequired, tfaRequired, verifiedRequired }, - loaders: { loadDomainByDomain, loadOrgByKey, loadTagByTagId }, + dataSources: { domain: domainDS, tags: tagsDS, organization: orgDS, auditLogs }, validators: { cleanseInput }, }, ) => { @@ -74,7 +71,7 @@ export const createDomain = new mutationWithClientMutationId({ let tags if (typeof args.tags !== 'undefined') { - tags = await loadTagByTagId.loadMany( + tags = await tagsDS.byTagId.loadMany( args.tags.map((tag) => { return cleanseInput(tag) }), @@ -106,7 +103,7 @@ export const createDomain = new mutationWithClientMutationId({ const cvdEnrollment = args.cvdEnrollment || { status: 'not-enrolled' } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to create a domain to an organization: ${orgId} that does not exist.`) @@ -200,67 +197,7 @@ export const createDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - let domainCursor - try { - domainCursor = await trx.step( - () => - query` - UPSERT { domain: ${insertDomain.domain} } - INSERT ${insertDomain} - UPDATE { } - IN domains - RETURN NEW - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - let insertedDomain - try { - insertedDomain = await domainCursor.next() - } catch (err) { - console.error(`Cursor error occurred for user: ${userKey} when inserting new domain: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - try { - await trx.step( - () => - query` - WITH claims - INSERT { - _from: ${org._id}, - _to: ${insertedDomain._id}, - tags: ${tags}, - assetState: ${assetState}, - firstSeen: ${new Date().toISOString()}, - } INTO claims - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain edge: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domain: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to create domain. Please try again.`)) - } - - // Clear dataloader incase anything was updated or inserted into domain - await loadDomainByDomain.clear(insertDomain.domain) - const returnDomain = await loadDomainByDomain.load(insertDomain.domain) + const returnDomain = await domainDS.create({ insertDomain, org, tags, assetState }) console.info(`User: ${userKey} successfully created ${returnDomain.domain} in org: ${org.slug}.`) @@ -289,10 +226,7 @@ export const createDomain = new mutationWithClientMutationId({ }) } - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -306,8 +240,8 @@ export const createDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', }, }) diff --git a/api/src/domain/mutations/favourite-domain.js b/api/src/domain/mutations/favourite-domain.js index e70014c179..2da270f542 100644 --- a/api/src/domain/mutations/favourite-domain.js +++ b/api/src/domain/mutations/favourite-domain.js @@ -25,11 +25,9 @@ export const favouriteDomain = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, auth: { userRequired, verifiedRequired }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -41,7 +39,7 @@ export const favouriteDomain = new mutationWithClientMutationId({ const { type: _domainType, id: domainId } = fromGlobalId(cleanseInput(args.domainId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { console.warn(`User: ${userKey} attempted to favourite ${domainId} however no domain is associated with that id.`) @@ -83,33 +81,7 @@ export const favouriteDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - () => - query` - WITH favourites - INSERT { - _from: ${user._id}, - _to: ${domain._id}, - } INTO favourites - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when inserting new domain edge: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domain: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to favourite domain. Please try again.`)) - } + await domainDataSource.favourite({ domain, user }) console.info(`User: ${userKey} successfully favourited domain ${domain.domain}.`) diff --git a/api/src/domain/mutations/ignore-cve.js b/api/src/domain/mutations/ignore-cve.js index 535854be0f..1098e00e90 100644 --- a/api/src/domain/mutations/ignore-cve.js +++ b/api/src/domain/mutations/ignore-cve.js @@ -4,7 +4,6 @@ import { t } from '@lingui/macro' import { CveID } from '../../scalars' import { ignoreCveUnion } from '../unions/ignore-cve-union' -import { logActivity } from '../../audit-logs' export const ignoreCve = new mutationWithClientMutationId({ name: 'IgnoreCve', @@ -31,13 +30,11 @@ export const ignoreCve = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { userRequired, checkSuperAdmin, superAdminRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource, auditLogs }, }, ) => { // Get User @@ -54,7 +51,7 @@ export const ignoreCve = new mutationWithClientMutationId({ const ignoredCve = cleanseInput(args.ignoredCve) // Check to see if domain exists - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) if (typeof domain === 'undefined') { console.warn(`User: "${userKey}" attempted to ignore CVE "${ignoredCve}" on unknown domain: "${domainId}".`) @@ -80,74 +77,7 @@ export const ignoreCve = new mutationWithClientMutationId({ const newIgnoredCves = Array.from(new Set([...oldIgnoredCves, ignoredCve])) - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - async () => - await query` - UPSERT { _key: ${domain._key} } - INSERT ${{ ignoredCves: newIgnoredCves }} - UPDATE ${{ ignoredCves: newIgnoredCves }} - IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domainId}", error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to ignore CVE. Please try again.`)) - } - - let currentDomainVulnerabilitiesCursor - try { - currentDomainVulnerabilitiesCursor = await trx.step( - () => query` - FOR finding IN additionalFindings - FILTER finding.domain == ${domain._id} - LIMIT 1 - FOR wc IN finding.webComponents - FILTER LENGTH(wc.WebComponentCves) > 0 - FOR vuln IN wc.WebComponentCves - FILTER vuln.Cve NOT IN ${newIgnoredCves} - RETURN DISTINCT vuln.Cve - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domainId}" when getting current CVEs, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to ignore CVE. Please try again.`)) - } - - try { - await trx.step( - () => - query` - UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domainId}" when updating domain, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to ignore CVE. Please try again.`)) - } - - // Commit transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred when user: "${userKey}" attempted to ignore CVE "${ignoredCve}" on domain "${domainId}", error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to ignore CVE. Please try again.`)) - } + const returnDomain = await domainDataSource.ignoreCve({ domain, ignoredCve, newIgnoredCves }) // Get all verified claims to domain and activityLog those organizations try { @@ -160,10 +90,7 @@ export const ignoreCve = new mutationWithClientMutationId({ } ` for await (const org of orgs) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -189,10 +116,7 @@ export const ignoreCve = new mutationWithClientMutationId({ }) } // Log activity for super admin logging - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -218,10 +142,6 @@ export const ignoreCve = new mutationWithClientMutationId({ ) } - // Clear dataloader and load updated domain - await loadDomainByKey.clear(domain._key) - const returnDomain = await loadDomainByKey.load(domain._key) - console.info(`User: "${userKey}" successfully ignored CVE "${ignoredCve}" on domain: "${domainId}".`) returnDomain.id = returnDomain._key diff --git a/api/src/domain/mutations/remove-domain.js b/api/src/domain/mutations/remove-domain.js index bde0ff54af..b906171ae9 100644 --- a/api/src/domain/mutations/remove-domain.js +++ b/api/src/domain/mutations/remove-domain.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeDomainUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { DomainRemovalReasonEnum } from '../../enums' import ac from '../../access-control' @@ -36,13 +35,12 @@ export const removeDomain = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey, loadOrgByKey }, + dataSources: { domain: domainDataSource, auditLogs }, + loaders: { loadOrgByKey }, }, ) => { // Get User @@ -56,7 +54,7 @@ export const removeDomain = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { @@ -154,202 +152,15 @@ export const removeDomain = new mutationWithClientMutationId({ throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) } - // Setup Transaction - const trx = await transaction(collections) - - if (dmarcCountCursor.count === 1) { - try { - await trx.step( - () => query` - WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries - LET dmarcSummaryEdges = ( - FOR v, e IN 1..1 OUTBOUND ${domain._id} domainsToDmarcSummaries - RETURN { edgeKey: e._key, dmarcSummaryId: e._to } - ) - LET removeDmarcSummaryEdges = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries - OPTIONS { waitForSync: true } - ) - LET removeDmarcSummary = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key - REMOVE key IN dmarcSummaries - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred when removing dmarc summary data for user: ${userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - await trx.step( - () => query` - WITH ownership, organizations, domains - LET domainEdges = ( - FOR v, e IN 1..1 INBOUND ${domain._id} ownership - REMOVE e._key IN ownership - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred when removing ownership data for user: ${userKey} while attempting to remove domain: ${domain.domain}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } - - if (countCursor.count <= 1) { - // Remove scan data - - try { - // Remove web data - await trx.step(async () => { - await query` - WITH web, webScan, domains - FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb - LET removeWebScansQuery = ( - FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans - REMOVE webScanV IN webScan - REMOVE webToWebScansV IN webToWebScans - OPTIONS { waitForSync: true } - ) - REMOVE webV IN web - REMOVE domainsWebEdge IN domainsWeb - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove web data for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - // Remove DNS data - await trx.step(async () => { - await query` - WITH dns, domains - FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS - REMOVE dnsV IN dns - REMOVE domainsDNSEdge IN domainsDNS - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DNS data for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - // remove favourites - try { - await trx.step(async () => { - await query` - WITH favourites, domains - FOR fav IN favourites - FILTER fav._to == ${domain._id} - REMOVE fav IN favourites - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove favourites for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - // remove DKIM selectors - try { - await trx.step(async () => { - await query` - FOR e IN domainsToSelectors - FILTER e._from == ${domain._id} - REMOVE e IN domainsToSelectors - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - - try { - // Remove domain - await trx.step(async () => { - await query` - FOR claim IN claims - FILTER claim._to == ${domain._id} - REMOVE claim IN claims - REMOVE ${domain} IN domains - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove domain ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } else { - try { - await trx.step(async () => { - await query` - WITH claims, domains, organizations - LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} claims RETURN { _key: e._key, _from: e._from, _to: e._to }) - LET edgeKeys = ( - FOR domainEdge IN domainEdges - FILTER domainEdge._to == ${domain._id} - FILTER domainEdge._from == ${org._id} - RETURN domainEdge._key - ) - FOR edgeKey IN edgeKeys - REMOVE edgeKey IN claims - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove claim for ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } - } - - // Commit transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred while user: ${userKey} attempted to remove ${domain.domain} in org: ${org.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove domain. Please try again.`)) - } + await domainDataSource.remove({ + domain, + org, + orgsClaimingDomain: orgsClaimingDomain.length, + hasOwnership: dmarcCountCursor.count === 1, + }) console.info(`User: ${userKey} successfully removed domain: ${domain.domain} from org: ${org.slug}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -362,8 +173,8 @@ export const removeDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', }, reason: args.reason, }) diff --git a/api/src/domain/mutations/remove-organizations-domains.js b/api/src/domain/mutations/remove-organizations-domains.js index 3934f8a4ec..15dc9c982d 100644 --- a/api/src/domain/mutations/remove-organizations-domains.js +++ b/api/src/domain/mutations/remove-organizations-domains.js @@ -45,7 +45,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS }, }, ) => { // Get User @@ -78,7 +78,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ } // Get Org from db - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) // Check to see if org exists if (typeof org === 'undefined') { @@ -136,7 +136,7 @@ export const removeOrganizationsDomains = new mutationWithClientMutationId({ const trx = await transaction(collections) // Get domain from db - const checkDomain = await loadDomainByDomain.load(domain) + const checkDomain = await domainDS.byDomain.load(domain) // Check to see if domain exists if (typeof checkDomain === 'undefined') { diff --git a/api/src/domain/mutations/request-discovery.js b/api/src/domain/mutations/request-discovery.js index 237ca9c30c..ac31a0f9ab 100644 --- a/api/src/domain/mutations/request-discovery.js +++ b/api/src/domain/mutations/request-discovery.js @@ -36,7 +36,7 @@ export const requestDiscovery = new mutationWithClientMutationId({ publish, request: { ip }, auth: { checkDomainPermission, userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, - loaders: { loadDomainByDomain, loadOrgByKey }, + dataSources: { domain: domainDS, organization: orgDS }, validators: { cleanseInput }, }, ) => { @@ -64,7 +64,7 @@ export const requestDiscovery = new mutationWithClientMutationId({ } // Check to see if domain exists - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDS.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn( @@ -74,7 +74,7 @@ export const requestDiscovery = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn( diff --git a/api/src/domain/mutations/request-scan.js b/api/src/domain/mutations/request-scan.js index c75eff28ce..f56565c93b 100644 --- a/api/src/domain/mutations/request-scan.js +++ b/api/src/domain/mutations/request-scan.js @@ -33,8 +33,7 @@ export const requestScan = new mutationWithClientMutationId({ request: { ip }, publish, auth: { checkDomainPermission, userRequired, verifiedRequired }, - loaders: { loadDomainByDomain }, - dataSources: { webScan }, + dataSources: { webScan, domain: domainDS }, validators: { cleanseInput }, }, ) => { @@ -46,7 +45,7 @@ export const requestScan = new mutationWithClientMutationId({ const domainInput = cleanseInput(args.domain) // Check to see if domain exists - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDS.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn( diff --git a/api/src/domain/mutations/unfavourite-domain.js b/api/src/domain/mutations/unfavourite-domain.js index 8d8738d94e..3f3691a061 100644 --- a/api/src/domain/mutations/unfavourite-domain.js +++ b/api/src/domain/mutations/unfavourite-domain.js @@ -25,11 +25,9 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, auth: { userRequired, verifiedRequired }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -41,7 +39,7 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ const { type: _domainType, id: domainId } = fromGlobalId(cleanseInput(args.domainId)) // Get domain from db - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) // Check to see if domain exists if (typeof domain === 'undefined') { console.warn( @@ -85,39 +83,7 @@ export const unfavouriteDomain = new mutationWithClientMutationId({ } } - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - () => - query` - WITH favourites, domains, users - LET domainEdges = (FOR v, e IN 1..1 INBOUND ${domain._id} favourites RETURN { _key: e._key, _from: e._from, _to: e._to }) - LET edgeKeys = ( - FOR domainEdge IN domainEdges - FILTER domainEdge._to == ${domain._id} - FILTER domainEdge._from == ${user._id} - RETURN domainEdge._key - ) - FOR edgeKey IN edgeKeys - REMOVE edgeKey IN favourites - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error(`Transaction step error occurred for user: ${userKey} when removing domain edge: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to unfavourite domain. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was unfavouriting domain: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to unfavourite domain. Please try again.`)) - } + await domainDataSource.unfavourite({ domain, user }) console.info(`User: ${userKey} successfully removed domain ${domain.domain} from favourites.`) diff --git a/api/src/domain/mutations/unignore-cve.js b/api/src/domain/mutations/unignore-cve.js index b4f82f4cd1..51a62aa186 100644 --- a/api/src/domain/mutations/unignore-cve.js +++ b/api/src/domain/mutations/unignore-cve.js @@ -4,7 +4,6 @@ import { t } from '@lingui/macro' import { CveID } from '../../scalars' import { ignoreCveUnion } from '../unions/ignore-cve-union' -import { logActivity } from '../../audit-logs' export const unignoreCve = new mutationWithClientMutationId({ name: 'UnignoreCve', @@ -31,13 +30,11 @@ export const unignoreCve = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { userRequired, checkSuperAdmin, superAdminRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey }, + dataSources: { domain: domainDataSource, auditLogs }, }, ) => { // Get User @@ -54,7 +51,7 @@ export const unignoreCve = new mutationWithClientMutationId({ const ignoredCve = cleanseInput(args.ignoredCve) // Check to see if domain exists - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) if (typeof domain === 'undefined') { console.warn(`User: "${userKey}" attempted to unignore CVE "${ignoredCve}" on unknown domain: "${domainId}".`) @@ -80,74 +77,7 @@ export const unignoreCve = new mutationWithClientMutationId({ const newIgnoredCves = Array.from(new Set([...oldIgnoredCves.filter((cve) => cve !== ignoredCve)])) - // Setup Transaction - const trx = await transaction(collections) - - try { - await trx.step( - async () => - await query` - UPSERT { _key: ${domain._key} } - INSERT ${{ ignoredCves: newIgnoredCves }} - UPDATE ${{ ignoredCves: newIgnoredCves }} - IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domainId}", error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to stop ignoring CVE. Please try again.`)) - } - - let currentDomainVulnerabilitiesCursor - try { - currentDomainVulnerabilitiesCursor = await trx.step( - () => query` - FOR finding IN additionalFindings - FILTER finding.domain == ${domain._id} - LIMIT 1 - FOR wc IN finding.webComponents - FILTER LENGTH(wc.WebComponentCves) > 0 - FOR vuln IN wc.WebComponentCves - FILTER vuln.Cve NOT IN ${newIgnoredCves} - RETURN DISTINCT vuln.Cve - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domainId}" when getting current CVEs, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to stop ignoring CVE. Please try again.`)) - } - - try { - await trx.step( - () => - query` - UPDATE { _key: ${domain._key}, cveDetected: ${currentDomainVulnerabilitiesCursor.count > 0} } IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domainId}" when updating domain, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to stop ignoring CVE. Please try again.`)) - } - - // Commit transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred when user: "${userKey}" attempted to unignore CVE "${ignoredCve}" on domain "${domainId}", error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to stop ignoring CVE. Please try again.`)) - } + const returnDomain = await domainDataSource.unignoreCve({ domain, ignoredCve, newIgnoredCves }) // Get all verified claims to domain and activityLog those organizations try { @@ -160,10 +90,7 @@ export const unignoreCve = new mutationWithClientMutationId({ } ` for await (const org of orgs) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -189,10 +116,7 @@ export const unignoreCve = new mutationWithClientMutationId({ }) } // Log activity for super admin logging - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -218,10 +142,6 @@ export const unignoreCve = new mutationWithClientMutationId({ ) } - // Clear dataloader and load updated domain - await loadDomainByKey.clear(domain._key) - const returnDomain = await loadDomainByKey.load(domain._key) - console.info(`User: "${userKey}" successfully unignored CVE "${ignoredCve}" on domain: "${domainId}".`) returnDomain.id = returnDomain._key diff --git a/api/src/domain/mutations/update-domain.js b/api/src/domain/mutations/update-domain.js index db5fa6899e..bf5e5493c3 100644 --- a/api/src/domain/mutations/update-domain.js +++ b/api/src/domain/mutations/update-domain.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { updateDomainUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import { AssetStateEnums } from '../../enums' import { CvdEnrollmentInputOptions } from '../../additional-findings/input/cvd-enrollment-options' import ac from '../../access-control' @@ -54,13 +53,11 @@ export const updateDomain = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, validators: { cleanseInput }, - loaders: { loadDomainByKey, loadOrgByKey, loadTagByTagId }, + dataSources: { domain: domainDataSource, auditLogs, tags: tagsDS, organization: orgDS }, }, ) => { // Get User @@ -74,7 +71,7 @@ export const updateDomain = new mutationWithClientMutationId({ let tags if (typeof args.tags !== 'undefined') { - tags = await loadTagByTagId.loadMany( + tags = await tagsDS.byTagId.loadMany( args.tags.map((tag) => { return cleanseInput(tag) }), @@ -111,7 +108,7 @@ export const updateDomain = new mutationWithClientMutationId({ } // Check to see if domain exists - const domain = await loadDomainByKey.load(domainId) + const domain = await domainDataSource.byKey.load(domainId) if (typeof domain === 'undefined') { console.warn( @@ -125,7 +122,7 @@ export const updateDomain = new mutationWithClientMutationId({ } // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn( @@ -189,35 +186,6 @@ export const updateDomain = new mutationWithClientMutationId({ cvdEnrollment.status = cvdEnrollment.status === 'enrolled' ? 'pending' : 'not-enrolled' } - // Setup Transaction - const trx = await transaction(collections) - - // Update domain - const domainToInsert = { - archived: typeof archived !== 'undefined' ? archived : domain?.archived, - ignoreRua: typeof args.ignoreRua !== 'undefined' ? args.ignoreRua : domain?.ignoreRua, - cvdEnrollment: typeof cvdEnrollment !== 'undefined' ? cvdEnrollment : domain?.cvdEnrollment, - } - - try { - await trx.step( - async () => - await query` - WITH domains - UPSERT { _key: ${domain._key} } - INSERT ${domainToInsert} - UPDATE ${domainToInsert} - IN domains - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain: ${domainId}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) - } - let claimCursor try { claimCursor = await query` @@ -236,45 +204,19 @@ export const updateDomain = new mutationWithClientMutationId({ console.error(`Cursor error occurred when user: ${userKey} running loadDomainByKey: ${err}`) } + const domainToInsert = { + archived: typeof archived !== 'undefined' ? archived : domain?.archived, + ignoreRua: typeof args.ignoreRua !== 'undefined' ? args.ignoreRua : domain?.ignoreRua, + cvdEnrollment: typeof cvdEnrollment !== 'undefined' ? cvdEnrollment : domain?.cvdEnrollment, + } + const claimToInsert = { tags: tags || claim?.tags, firstSeen: typeof claim?.firstSeen === 'undefined' ? new Date().toISOString() : claim?.firstSeen, assetState: assetState || claim?.assetState, } - try { - await trx.step( - async () => - await query` - WITH claims - UPSERT { _from: ${org._id}, _to: ${domain._id} } - INSERT ${claimToInsert} - UPDATE ${claimToInsert} - IN claims - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain edge, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to update domain edge. Please try again.`)) - } - - // Commit transaction - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction commit error occurred when user: ${userKey} attempted to update domain: ${domainId}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to update domain. Please try again.`)) - } - - // Clear dataloader and load updated domain - await loadDomainByKey.clear(domain._key) - const returnDomain = await loadDomainByKey.load(domain._key) + const returnDomain = await domainDataSource.update({ domain, org, domainToInsert, claimToInsert }) console.info(`User: ${userKey} successfully updated domain: ${domainId}.`) @@ -304,10 +246,7 @@ export const updateDomain = new mutationWithClientMutationId({ } if (updatedProperties.length > 0) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -320,18 +259,15 @@ export const updateDomain = new mutationWithClientMutationId({ organization: { id: org._key, name: org.name, - }, // name of resource being acted upon - resourceType: 'domain', // user, org, domain + }, + resourceType: 'domain', updatedProperties, }, }) } if (typeof archived !== 'undefined') { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -341,7 +277,7 @@ export const updateDomain = new mutationWithClientMutationId({ action: 'update', target: { resource: domain.domain, - resourceType: 'domain', // user, org, domain + resourceType: 'domain', updatedProperties: [{ name: 'archived', oldValue: domain.archived, newValue: archived }], }, }) diff --git a/api/src/domain/mutations/update-domains-by-domain-ids.js b/api/src/domain/mutations/update-domains-by-domain-ids.js index 17d0fad6c2..34dd35f8a6 100644 --- a/api/src/domain/mutations/update-domains-by-domain-ids.js +++ b/api/src/domain/mutations/update-domains-by-domain-ids.js @@ -2,7 +2,6 @@ import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' import { bulkModifyDomainsUnion } from '../unions' import { GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql' import { t } from '@lingui/macro' -import { logActivity } from '../../audit-logs' import ac from '../../access-control' export const updateDomainsByDomainIds = new mutationWithClientMutationId({ @@ -34,12 +33,10 @@ export const updateDomainsByDomainIds = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadTagByTagId, loadOrgByKey }, + dataSources: { domain: domainDS, auditLogs, tags: tagsDS, organization: orgDS }, validators: { cleanseInput }, }, ) => { @@ -50,7 +47,7 @@ export const updateDomainsByDomainIds = new mutationWithClientMutationId({ // Cleanse input const { id: orgId } = fromGlobalId(cleanseInput(args.orgId)) - let tags = (await loadTagByTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] + let tags = (await tagsDS.byTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] tags = tags .filter( ({ visible, ownership, organizations }) => @@ -59,7 +56,7 @@ export const updateDomainsByDomainIds = new mutationWithClientMutationId({ .map((tag) => tag.tagId) // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to update domains to an organization: ${orgId} that does not exist.`) return { @@ -115,45 +112,18 @@ export const updateDomainsByDomainIds = new mutationWithClientMutationId({ continue } - // Setup Transaction - const trx = await transaction(collections) const { claim, domain } = checkClaim const claimToInsert = { tags: [...new Set([...claim.tags, ...tags])], } try { - await trx.step( - async () => - await query` - WITH claims - UPSERT { _key: ${claim._key} } - INSERT ${claimToInsert} - UPDATE ${claimToInsert} - IN claims - `, - ) - } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain edge, error: ${err}`, - ) - await trx.abort() - continue - } - - // commit and log - try { - await trx.commit() + await domainDS.updateClaim({ claim, claimToInsert }) } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domains: ${err}`) - await trx.abort() continue } - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, diff --git a/api/src/domain/mutations/update-domains-by-filters.js b/api/src/domain/mutations/update-domains-by-filters.js index 743d7a7950..d428d5c743 100644 --- a/api/src/domain/mutations/update-domains-by-filters.js +++ b/api/src/domain/mutations/update-domains-by-filters.js @@ -2,7 +2,6 @@ import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay' import { bulkModifyDomainsUnion } from '../unions' import { GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql' import { t } from '@lingui/macro' -import { logActivity } from '../../audit-logs' import { domainFilter } from '../inputs' import { aql } from 'arangojs' import ac from '../../access-control' @@ -40,12 +39,10 @@ export const updateDomainsByFilters = new mutationWithClientMutationId({ { i18n, query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired, tfaRequired }, - loaders: { loadTagByTagId, loadOrgByKey }, + dataSources: { domain: domainDS, auditLogs, tags: tagsDS, organization: orgDS }, validators: { cleanseInput }, }, ) => { @@ -57,7 +54,7 @@ export const updateDomainsByFilters = new mutationWithClientMutationId({ // Cleanse input const { id: orgId } = fromGlobalId(cleanseInput(args.orgId)) const search = cleanseInput(args.search) - let tags = (await loadTagByTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] + let tags = (await tagsDS.byTagId.loadMany(args.tags.map((tag) => cleanseInput(tag)))) ?? [] tags = tags .filter( ({ visible, ownership, organizations }) => @@ -67,7 +64,7 @@ export const updateDomainsByFilters = new mutationWithClientMutationId({ const filters = args.filters // Check to see if org exists - const org = await loadOrgByKey.load(orgId) + const org = await orgDS.byKey.load(orgId) if (typeof org === 'undefined') { console.warn(`User: ${userKey} attempted to update domains to an organization: ${orgId} that does not exist.`) return { @@ -246,45 +243,18 @@ export const updateDomainsByFilters = new mutationWithClientMutationId({ let domainCount = 0 for (const checkClaim of checkClaims) { - // Setup Transaction - const trx = await transaction(collections) const { claim, domain } = checkClaim const claimToInsert = { tags: [...new Set([...claim.tags, ...tags])], } try { - await trx.step( - async () => - await query` - WITH claims - UPSERT { _key: ${claim._key} } - INSERT ${claimToInsert} - UPDATE ${claimToInsert} - IN claims - `, - ) + await domainDS.updateClaim({ claim, claimToInsert }) } catch (err) { - console.error( - `Transaction step error occurred when user: ${userKey} attempted to update domain edge, error: ${err}`, - ) - await trx.abort() continue } - // commit and log - try { - await trx.commit() - } catch (err) { - console.error(`Transaction commit error occurred while user: ${userKey} was creating domains: ${err}`) - await trx.abort() - continue - } - - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, diff --git a/api/src/domain/objects/__tests__/domain.test.js b/api/src/domain/objects/__tests__/domain.test.js index ace7657db6..9e4f32ca8b 100644 --- a/api/src/domain/objects/__tests__/domain.test.js +++ b/api/src/domain/objects/__tests__/domain.test.js @@ -223,9 +223,9 @@ describe('given the domain object', () => { }, dataSources: { auth: { domainPermissionByDomainId: { load: jest.fn().mockResolvedValue(true) } }, - }, - loaders: { - loadDkimSelectorsByDomainId: jest.fn().mockReturnValue(selectors), + domain: { + dkimSelectorsByDomainId: jest.fn().mockReturnValue(selectors), + }, }, }, ), @@ -306,8 +306,10 @@ describe('given the domain object', () => { { _id: '1' }, { first: 1 }, { - loaders: { - loadOrgConnectionsByDomainId: jest.fn().mockReturnValue(expectedResult), + dataSources: { + organization: { + connectionsByDomainId: jest.fn().mockReturnValue(expectedResult), + }, }, auth: { checkSuperAdmin: jest.fn().mockReturnValue(false), diff --git a/api/src/domain/objects/domain.js b/api/src/domain/objects/domain.js index 40e800be16..4845bd22fb 100644 --- a/api/src/domain/objects/domain.js +++ b/api/src/domain/objects/domain.js @@ -56,7 +56,7 @@ export const domainType = new GraphQLObjectType({ resolve: async ( { _id }, _, - { userKey, auth: { userRequired }, dataSources: { auth: authDS }, loaders: { loadDkimSelectorsByDomainId } }, + { userKey, auth: { userRequired }, dataSources: { auth: authDS, domain: domainDataSource } }, ) => { await userRequired() const permitted = await authDS.domainPermissionByDomainId.load(_id) @@ -65,7 +65,7 @@ export const domainType = new GraphQLObjectType({ throw new Error(t`Cannot query domain selectors without permission.`) } - return await loadDkimSelectorsByDomainId({ + return await domainDataSource.dkimSelectorsByDomainId({ domainId: _id, }) }, @@ -122,10 +122,10 @@ export const domainType = new GraphQLObjectType({ ...connectionArgs, }, description: 'The organization that this domain belongs to.', - resolve: async ({ _id }, args, { auth: { checkSuperAdmin }, loaders: { loadOrgConnectionsByDomainId } }) => { + resolve: async ({ _id }, args, { auth: { checkSuperAdmin }, dataSources: { organization } }) => { const isSuperAdmin = await checkSuperAdmin() - return await loadOrgConnectionsByDomainId({ + return await organization.connectionsByDomainId({ domainId: _id, isSuperAdmin, ...args, @@ -154,11 +154,7 @@ export const domainType = new GraphQLObjectType({ ...connectionArgs, }, description: `DNS scan results.`, - resolve: async ( - { _id }, - args, - { userKey, auth: { userRequired }, dataSources: { auth: authDS, dnsScan } }, - ) => { + resolve: async ({ _id }, args, { userKey, auth: { userRequired }, dataSources: { auth: authDS, dnsScan } }) => { await userRequired() const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { @@ -200,11 +196,7 @@ export const domainType = new GraphQLObjectType({ }, ...connectionArgs, }, - resolve: async ( - { _id }, - args, - { userKey, auth: { userRequired }, dataSources: { auth: authDS, webScan } }, - ) => { + resolve: async ({ _id }, args, { userKey, auth: { userRequired }, dataSources: { auth: authDS, webScan } }) => { await userRequired() const permitted = await authDS.domainPermissionByDomainId.load(_id) if (!permitted) { @@ -292,7 +284,7 @@ export const domainType = new GraphQLObjectType({ return { domainKey: _key, _id: dmarcSummaryEdge._to, - startDate: startDate, + startDate, } }, }, @@ -338,8 +330,8 @@ export const domainType = new GraphQLObjectType({ defaultValue: true, }, }, - resolve: async ({ claimTags }, args, { loaders: { loadTagByTagId } }) => { - const loadedTags = await loadTagByTagId.loadMany(claimTags) + resolve: async ({ claimTags }, args, { dataSources: { tags } }) => { + const loadedTags = await tags.byTagId(claimTags) return loadedTags.filter((tag) => { return args.isVisible ? tag.visible : true }) diff --git a/api/src/domain/queries/__tests__/find-domain-by-domain.test.js b/api/src/domain/queries/__tests__/find-domain-by-domain.test.js index dea6da436b..2c93995109 100644 --- a/api/src/domain/queries/__tests__/find-domain-by-domain.test.js +++ b/api/src/domain/queries/__tests__/find-domain-by-domain.test.js @@ -10,7 +10,7 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { checkDomainPermission, userRequired, verifiedRequired, AuthDataSource } from '../../../auth' -import { loadDkimSelectorsByDomainId, loadDomainByDomain } from '../../loaders' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user/loaders' import dbschema from '../../../../database.json' @@ -157,20 +157,13 @@ describe('given findDomainByDomain query', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), + domain: new DomainDataSource({ query, userKey: user._key }), }, validators: { cleanseInput, }, loaders: { - loadDomainByDomain: loadDomainByDomain({ query }), loadUserByKey: loadUserByKey({ query }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey: user._key, - cleanseInput, - i18n, - auth: { loginRequiredBool: true }, - }), }, }, }) @@ -250,9 +243,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue(undefined), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue(undefined) }, }, }, }, @@ -301,9 +294,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ _id: '1' }), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue({ _id: '1' }) }, }, }, }, @@ -365,9 +358,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue(undefined), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue(undefined) }, }, }, }, @@ -416,9 +409,9 @@ describe('given findDomainByDomain query', () => { validators: { cleanseInput, }, - loaders: { - loadDomainByDomain: { - load: jest.fn().mockReturnValue({ _id: '1' }), + dataSources: { + domain: { + byDomain: { load: jest.fn().mockReturnValue({ _id: '1' }) }, }, }, }, diff --git a/api/src/domain/queries/__tests__/find-my-domains.test.js b/api/src/domain/queries/__tests__/find-my-domains.test.js index db218facd9..04f0729d7e 100644 --- a/api/src/domain/queries/__tests__/find-my-domains.test.js +++ b/api/src/domain/queries/__tests__/find-my-domains.test.js @@ -10,7 +10,7 @@ import { createQuerySchema } from '../../../query' import { createMutationSchema } from '../../../mutation' import { cleanseInput } from '../../../validators' import { AuthDataSource, checkDomainPermission, checkSuperAdmin, userRequired, verifiedRequired } from '../../../auth' -import { loadDkimSelectorsByDomainId, loadDomainConnectionsByUserId } from '../../loaders' +import { DomainDataSource } from '../../data-source' import { loadUserByKey } from '../../../user' import dbschema from '../../../../database.json' @@ -197,20 +197,12 @@ describe('given findMyDomainsQuery', () => { }, dataSources: { auth: new AuthDataSource({ query, userKey: user._key }), - }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ - query, - userKey: user._key, - cleanseInput, - auth: { loginRequired: true }, - }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ + domain: new DomainDataSource({ query, userKey: user._key, cleanseInput, i18n, - auth: { loginRequiredBool: true }, + loginRequiredBool: true, }), }, }, @@ -307,12 +299,12 @@ describe('given findMyDomainsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ + dataSources: { + domain: new DomainDataSource({ query: mockedQuery, userKey: 1, cleanseInput, - auth: { loginRequired: true }, + loginRequiredBool: true, i18n, }), }, @@ -382,12 +374,12 @@ describe('given findMyDomainsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ + dataSources: { + domain: new DomainDataSource({ query: mockedQuery, userKey: 1, cleanseInput, - auth: { loginRequired: true }, + loginRequiredBool: true, i18n, }), }, diff --git a/api/src/domain/queries/find-domain-by-domain.js b/api/src/domain/queries/find-domain-by-domain.js index ee896ae55e..9532217cc2 100644 --- a/api/src/domain/queries/find-domain-by-domain.js +++ b/api/src/domain/queries/find-domain-by-domain.js @@ -20,7 +20,7 @@ export const findDomainByDomain = { i18n, userKey, auth: { checkDomainPermission, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadDomainByDomain }, + dataSources: { domain: domainDataSource }, validators: { cleanseInput }, }, ) => { @@ -34,7 +34,7 @@ export const findDomainByDomain = { const domainInput = cleanseInput(args.domain) // Retrieve domain by domain - const domain = await loadDomainByDomain.load(domainInput) + const domain = await domainDataSource.byDomain.load(domainInput) if (typeof domain === 'undefined') { console.warn(`User ${userKey} could not retrieve domain.`) diff --git a/api/src/domain/queries/find-my-domains.js b/api/src/domain/queries/find-my-domains.js index 870013d1de..5ba1c7d7cb 100644 --- a/api/src/domain/queries/find-my-domains.js +++ b/api/src/domain/queries/find-my-domains.js @@ -36,7 +36,7 @@ export const findMyDomains = { { userKey, auth: { checkSuperAdmin, userRequired, loginRequiredBool, verifiedRequired }, - loaders: { loadDomainConnectionsByUserId }, + dataSources: { domain: domainDataSource }, }, ) => { if (loginRequiredBool) { @@ -46,7 +46,7 @@ export const findMyDomains = { const isSuperAdmin = await checkSuperAdmin() - const domainConnections = await loadDomainConnectionsByUserId({ + const domainConnections = await domainDataSource.connectionsByUserId({ isSuperAdmin, ...args, }) diff --git a/api/src/initialize-loaders.js b/api/src/initialize-loaders.js index 9345b654cf..5e08e47969 100644 --- a/api/src/initialize-loaders.js +++ b/api/src/initialize-loaders.js @@ -15,23 +15,7 @@ import { loadDmarcYearlySumEdge, loadAllVerifiedRuaDomains, } from './dmarc-summaries/loaders' -import { - loadDomainByKey, - loadDomainByDomain, - loadDomainConnectionsByOrgId, - loadDomainConnectionsByUserId, - loadDkimSelectorsByDomainId, -} from './domain/loaders' -import { - loadOrgByKey, - loadOrgBySlug, - loadOrgConnectionsByDomainId, - loadOrgConnectionsByUserId, - loadAllOrganizationDomainStatuses, - loadOrganizationDomainStatuses, - loadOrganizationSummariesByPeriod, - loadOrganizationNamesById, -} from './organization/loaders' +import { loadOrgByKey, loadOrganizationNamesById } from './organization/loaders' import { loadMyTrackerByUserId, loadUserByUserName, loadUserByKey, loadUserConnectionsByUserId } from './user/loaders' import { loadVerifiedDomainsById, @@ -45,22 +29,9 @@ import { loadVerifiedOrgConnectionsByDomainId, loadVerifiedOrgConnections, } from './verified-organizations/loaders' -import { loadTagByTagId, loadTagsByOrg } from './tags' export function initializeLoaders({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, moment }) { return { - loadTagByTagId: loadTagByTagId({ - query, - userKey, - i18n, - language, - }), - loadTagsByOrg: loadTagsByOrg({ - query, - userKey, - i18n, - language, - }), loadDkimFailConnectionsBySumId: loadDkimFailConnectionsBySumId({ query, userKey, @@ -109,87 +80,8 @@ export function initializeLoaders({ query, userKey, i18n, language, cleanseInput i18n, }), loadDmarcYearlySumEdge: loadDmarcYearlySumEdge({ query, userKey, i18n }), - loadDomainByDomain: loadDomainByDomain({ query, userKey, i18n }), - loadDomainByKey: loadDomainByKey({ query, userKey, i18n }), - loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ - query, - userKey, - language, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadDomainConnectionsByUserId: loadDomainConnectionsByUserId({ - query, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadDkimSelectorsByDomainId: loadDkimSelectorsByDomainId({ - query, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadOrgByKey: loadOrgByKey({ - query, - language, - userKey, - i18n, - }), - loadOrgBySlug: loadOrgBySlug({ - query, - language, - userKey, - i18n, - }), - loadOrgConnectionsByDomainId: loadOrgConnectionsByDomainId({ - query, - language, - userKey, - cleanseInput, - i18n, - auth: { loginRequiredBool }, - }), - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), - loadOrganizationSummariesByPeriod: loadOrganizationSummariesByPeriod({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), - loadOrganizationNamesById: loadOrganizationNamesById({ - query, - language, - userKey, - i18n, - }), - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ - query, - userKey, - cleanseInput, - language, - i18n, - }), - loadOrganizationDomainStatuses: loadOrganizationDomainStatuses({ - query, - userKey, - cleanseInput, - language, - i18n, - auth: { loginRequiredBool }, - }), + loadOrgByKey: loadOrgByKey({ query, language, userKey, i18n }), + loadOrganizationNamesById: loadOrganizationNamesById({ query, userKey, i18n }), loadMyTrackerByUserId: loadMyTrackerByUserId({ query, language, diff --git a/api/src/locale/en/messages.po b/api/src/locale/en/messages.po index 661738f401..554afd988e 100644 --- a/api/src/locale/en/messages.po +++ b/api/src/locale/en/messages.po @@ -19,8 +19,8 @@ msgstr "" #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 #: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:177 -#: src/domain/loaders/load-domain-connections-by-user-id.js:204 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:205 #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 #: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 #: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 @@ -66,8 +66,8 @@ msgstr "`{argSet}` on the `DmarcFailureTable` connection cannot be less than zer msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." msgstr "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:156 -#: src/domain/loaders/load-domain-connections-by-user-id.js:181 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 +#: src/domain/loaders/load-domain-connections-by-user-id.js:182 msgid "`{argSet}` on the `Domain` connection cannot be less than zero." msgstr "`{argSet}` on the `Domain` connection cannot be less than zero." @@ -123,33 +123,35 @@ msgstr "`{argSet}` on the `VerifiedDomain` connection cannot be less than zero." msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." msgstr "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." -#: src/organization/objects/organization.js:245 +#: src/organization/objects/organization.js:240 #: src/organization/queries/get-all-organization-domain-statuses.js:69 msgid "Assess" msgstr "Assess" -#: src/auth/check-permission.js:18 -#: src/auth/check-permission.js:57 -#: src/auth/user-required.js:10 -#: src/auth/user-required.js:21 -#: src/auth/user-required.js:28 +#: src/auth/checks/check-permission.js:18 +#: src/auth/checks/check-permission.js:57 +#: src/auth/guards/user-required.js:10 +#: src/auth/guards/user-required.js:21 +#: src/auth/guards/user-required.js:28 +#: src/auth/loaders/load-permission-by-org-id.js:19 +#: src/auth/loaders/load-permission-by-org-id.js:63 msgid "Authentication error. Please sign in." msgstr "Authentication error. Please sign in." -#: src/domain/objects/domain.js:237 +#: src/domain/objects/domain.js:229 msgid "Cannot query additional findings without permission." msgstr "Cannot query additional findings without permission." -#: src/organization/objects/organization.js:363 +#: src/organization/objects/organization.js:359 msgid "Cannot query affiliations on organization without admin permission or higher." msgstr "Cannot query affiliations on organization without admin permission or higher." #: src/audit-logs/loaders/load-audit-logs-by-org-id.js:224 -#: src/audit-logs/queries/find-audit-logs.js:52 +#: src/audit-logs/queries/find-audit-logs.js:53 msgid "Cannot query audit logs on organization without admin permission or higher." msgstr "Cannot query audit logs on organization without admin permission or higher." -#: src/domain/objects/domain.js:168 +#: src/domain/objects/domain.js:164 msgid "Cannot query dns scan results without permission." msgstr "Cannot query dns scan results without permission." @@ -157,7 +159,7 @@ msgstr "Cannot query dns scan results without permission." msgid "Cannot query domain selectors without permission." msgstr "Cannot query domain selectors without permission." -#: src/domain/objects/domain.js:214 +#: src/domain/objects/domain.js:206 msgid "Cannot query web scan results without permission." msgstr "Cannot query web scan results without permission." @@ -169,7 +171,7 @@ msgstr "CVE is already ignored for this domain." msgid "CVE is not ignored for this domain." msgstr "CVE is not ignored for this domain." -#: src/organization/objects/organization.js:247 +#: src/organization/objects/organization.js:242 #: src/organization/queries/get-all-organization-domain-statuses.js:71 msgid "Deploy" msgstr "Deploy" @@ -178,13 +180,13 @@ msgstr "Deploy" msgid "Email already in use." msgstr "Email already in use." -#: src/organization/objects/organization.js:249 +#: src/organization/objects/organization.js:244 #: src/organization/queries/get-all-organization-domain-statuses.js:73 msgid "Enforce" msgstr "Enforce" -#: src/domain/mutations/request-scan.js:89 -#: src/domain/mutations/request-scan.js:99 +#: src/domain/mutations/request-scan.js:90 +#: src/domain/mutations/request-scan.js:100 msgid "Error while requesting scan. Please try again." msgstr "Error while requesting scan. Please try again." @@ -211,11 +213,11 @@ msgstr "Incorrect token value. Please request a new email." msgid "Incorrect username or password. Please try again." msgstr "Incorrect username or password. Please try again." -#: src/auth/verify-jwt.js:15 +#: src/auth/utils/verify-jwt.js:15 msgid "Invalid token, please sign in." msgstr "Invalid token, please sign in." -#: src/organization/objects/organization.js:251 +#: src/organization/objects/organization.js:246 #: src/organization/queries/get-all-organization-domain-statuses.js:75 msgid "Maintain" msgstr "Maintain" @@ -241,22 +243,22 @@ msgstr "No verified domain with the provided domain could be found." msgid "No verified organization with the provided slug could be found." msgstr "No verified organization with the provided slug could be found." -#: src/organization/mutations/verify-organization.js:81 +#: src/organization/mutations/verify-organization.js:78 msgid "Organization has already been verified." msgstr "Organization has already been verified." -#: src/organization/mutations/update-organization.js:185 +#: src/organization/mutations/update-organization.js:167 msgid "Organization name already in use, please choose another and try again." msgstr "Organization name already in use, please choose another and try again." -#: src/organization/mutations/create-organization.js:89 +#: src/organization/mutations/create-organization.js:85 msgid "Organization name already in use. Please try again with a different name." msgstr "Organization name already in use. Please try again with a different name." -#: src/auth/check-domain-ownership.js:29 -#: src/auth/check-domain-ownership.js:39 -#: src/auth/check-domain-ownership.js:65 -#: src/auth/check-domain-ownership.js:74 +#: src/auth/checks/check-domain-ownership.js:29 +#: src/auth/checks/check-domain-ownership.js:39 +#: src/auth/checks/check-domain-ownership.js:65 +#: src/auth/checks/check-domain-ownership.js:74 msgid "Ownership check error. Unable to request domain information." msgstr "Ownership check error. Unable to request domain information." @@ -290,8 +292,8 @@ msgstr "Passing both `first` and `last` to paginate the `DmarcFailureTable` conn msgid "Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:147 -#: src/domain/loaders/load-domain-connections-by-user-id.js:172 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:148 +#: src/domain/loaders/load-domain-connections-by-user-id.js:173 msgid "Passing both `first` and `last` to paginate the `Domain` connection is not supported." msgstr "Passing both `first` and `last` to paginate the `Domain` connection is not supported." @@ -364,13 +366,17 @@ msgstr "Password was successfully updated." msgid "Passwords do not match." msgstr "Passwords do not match." -#: src/auth/check-domain-permission.js:22 -#: src/auth/check-domain-permission.js:51 -#: src/auth/check-domain-permission.js:61 +#: src/auth/checks/check-domain-permission.js:22 +#: src/auth/checks/check-domain-permission.js:51 +#: src/auth/checks/check-domain-permission.js:61 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:19 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:51 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:61 msgid "Permission check error. Unable to request domain information." msgstr "Permission check error. Unable to request domain information." #: src/organization/queries/find-organization-by-slug.js:50 +#: src/organization/queries/find-organization-by-slug.js:52 msgid "Permission Denied: Could not retrieve specified organization." msgstr "Permission Denied: Could not retrieve specified organization." @@ -386,7 +392,7 @@ msgstr "Permission Denied: Please contact org owner to transfer ownership." msgid "Permission Denied: Please contact organization admin for help with archiving domains." msgstr "Permission Denied: Please contact organization admin for help with archiving domains." -#: src/tags/mutations/create-tag.js:137 +#: src/tags/mutations/create-tag.js:131 msgid "Permission Denied: Please contact organization admin for help with creating tag." msgstr "Permission Denied: Please contact organization admin for help with creating tag." @@ -398,7 +404,7 @@ msgstr "Permission Denied: Please contact organization admin for help with remov msgid "Permission Denied: Please contact organization admin for help with removing domains." msgstr "Permission Denied: Please contact organization admin for help with removing domains." -#: src/organization/mutations/remove-organization.js:71 +#: src/organization/mutations/remove-organization.js:67 msgid "Permission Denied: Please contact organization admin for help with removing organization." msgstr "Permission Denied: Please contact organization admin for help with removing organization." @@ -412,12 +418,12 @@ msgstr "Permission Denied: Please contact organization admin for help with remov msgid "Permission Denied: Please contact organization admin for help with updating domains." msgstr "Permission Denied: Please contact organization admin for help with updating domains." -#: src/organization/mutations/update-organization.js:156 +#: src/organization/mutations/update-organization.js:152 msgid "Permission Denied: Please contact organization admin for help with updating organization." msgstr "Permission Denied: Please contact organization admin for help with updating organization." -#: src/tags/mutations/update-tag.js:147 -#: src/tags/mutations/update-tag.js:158 +#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:136 msgid "Permission Denied: Please contact organization admin for help with updating tag." msgstr "Permission Denied: Please contact organization admin for help with updating tag." @@ -448,12 +454,12 @@ msgstr "Permission Denied: Please contact organization user for help with creati #~ msgstr "Permission Denied: Please contact organization user for help with retrieving tags." #: src/domain/queries/find-domain-by-domain.js:51 -#: src/organization/objects/organization.js:199 +#: src/organization/objects/organization.js:195 msgid "Permission Denied: Please contact organization user for help with retrieving this domain." msgstr "Permission Denied: Please contact organization user for help with retrieving this domain." #: src/domain/mutations/request-discovery.js:98 -#: src/domain/mutations/request-scan.js:65 +#: src/domain/mutations/request-scan.js:66 msgid "Permission Denied: Please contact organization user for help with scanning this domain." msgstr "Permission Denied: Please contact organization user for help with scanning this domain." @@ -461,7 +467,7 @@ msgstr "Permission Denied: Please contact organization user for help with scanni msgid "Permission Denied: Please contact organization user for help with updating this domain." msgstr "Permission Denied: Please contact organization user for help with updating this domain." -#: src/organization/mutations/archive-organization.js:70 +#: src/organization/mutations/archive-organization.js:66 msgid "Permission Denied: Please contact super admin for help with archiving organization." msgstr "Permission Denied: Please contact super admin for help with archiving organization." @@ -470,7 +476,7 @@ msgstr "Permission Denied: Please contact super admin for help with archiving or msgid "Permission Denied: Please contact super admin for help with removing domain." msgstr "Permission Denied: Please contact super admin for help with removing domain." -#: src/organization/mutations/remove-organization.js:84 +#: src/organization/mutations/remove-organization.js:80 msgid "Permission Denied: Please contact super admin for help with removing organization." msgstr "Permission Denied: Please contact super admin for help with removing organization." @@ -478,11 +484,11 @@ msgstr "Permission Denied: Please contact super admin for help with removing org #~ msgid "Permission Denied: Please contact super admin for help with scanning this domain." #~ msgstr "Permission Denied: Please contact super admin for help with scanning this domain." -#: src/tags/mutations/update-tag.js:169 +#: src/tags/mutations/update-tag.js:147 msgid "Permission Denied: Please contact super admin for help with updating tag." msgstr "Permission Denied: Please contact super admin for help with updating tag." -#: src/affiliation/mutations/invite-user-to-org.js:111 +#: src/affiliation/mutations/invite-user-to-org.js:112 msgid "Permission Denied: Please contact super admin for help with user invitations." msgstr "Permission Denied: Please contact super admin for help with user invitations." @@ -490,14 +496,14 @@ msgstr "Permission Denied: Please contact super admin for help with user invitat msgid "Permission Denied: Please contact super admin for help with user role changes." msgstr "Permission Denied: Please contact super admin for help with user role changes." -#: src/organization/mutations/verify-organization.js:68 +#: src/organization/mutations/verify-organization.js:65 msgid "Permission Denied: Please contact super admin for help with verifying this organization." msgstr "Permission Denied: Please contact super admin for help with verifying this organization." -#: src/auth/check-user-is-admin-for-user.js:20 -#: src/auth/check-user-is-admin-for-user.js:30 -#: src/auth/check-user-is-admin-for-user.js:63 -#: src/auth/check-user-is-admin-for-user.js:73 +#: src/auth/checks/check-user-is-admin-for-user.js:20 +#: src/auth/checks/check-user-is-admin-for-user.js:30 +#: src/auth/checks/check-user-is-admin-for-user.js:63 +#: src/auth/checks/check-user-is-admin-for-user.js:73 msgid "Permission error, not an admin for this user." msgstr "Permission error, not an admin for this user." @@ -505,7 +511,7 @@ msgstr "Permission error, not an admin for this user." msgid "Permission error: Unable to close other user's account." msgstr "Permission error: Unable to close other user's account." -#: src/auth/super-admin-required.js:11 +#: src/auth/guards/super-admin-required.js:11 msgid "Permissions error. You do not have sufficient permissions to access this data." msgstr "Permissions error. You do not have sufficient permissions to access this data." @@ -567,8 +573,8 @@ msgstr "Requesting `{amount}` records on the `DmarcFailureTable` connection exce msgid "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:163 -#: src/domain/loaders/load-domain-connections-by-user-id.js:190 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:164 +#: src/domain/loaders/load-domain-connections-by-user-id.js:191 msgid "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." msgstr "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." @@ -622,7 +628,7 @@ msgstr "Successfully added {domainCount} domain(s) to {0}." #~ msgstr "Successfully added {domainCount} domains to {0}." #. placeholder {0}: organization.slug -#: src/organization/mutations/archive-organization.js:196 +#: src/organization/mutations/archive-organization.js:100 msgid "Successfully archived organization: {0}." msgstr "Successfully archived organization: {0}." @@ -631,7 +637,7 @@ msgstr "Successfully archived organization: {0}." msgid "Successfully closed account." msgstr "Successfully closed account." -#: src/domain/mutations/request-scan.js:174 +#: src/domain/mutations/request-scan.js:175 msgid "Successfully dispatched one time scan." msgstr "Successfully dispatched one time scan." @@ -647,7 +653,7 @@ msgstr "Successfully dispatched subdomain discovery scan." msgid "Successfully email verified account." msgstr "Successfully email verified account." -#: src/affiliation/mutations/invite-user-to-org.js:281 +#: src/affiliation/mutations/invite-user-to-org.js:282 msgid "Successfully invited user to organization, and sent notification email." msgstr "Successfully invited user to organization, and sent notification email." @@ -677,7 +683,7 @@ msgid "Successfully removed domain: {0} from favourites." msgstr "Successfully removed domain: {0} from favourites." #. placeholder {0}: organization.slug -#: src/organization/mutations/remove-organization.js:419 +#: src/organization/mutations/remove-organization.js:107 msgid "Successfully removed organization: {0}." msgstr "Successfully removed organization: {0}." @@ -689,7 +695,7 @@ msgstr "Successfully removed user from organization." msgid "Successfully requested invite to organization, and sent notification email." msgstr "Successfully requested invite to organization, and sent notification email." -#: src/affiliation/mutations/invite-user-to-org.js:169 +#: src/affiliation/mutations/invite-user-to-org.js:170 msgid "Successfully sent invitation to service, and organization email." msgstr "Successfully sent invitation to service, and organization email." @@ -711,7 +717,7 @@ msgid "Successfully updated {domainCount} domain(s) in {0} with {1}." msgstr "Successfully updated {domainCount} domain(s) in {0} with {1}." #. placeholder {0}: currentOrg.slug -#: src/organization/mutations/verify-organization.js:138 +#: src/organization/mutations/verify-organization.js:90 msgid "Successfully verified organization: {0}." msgstr "Successfully verified organization: {0}." @@ -723,9 +729,9 @@ msgstr "Successfully verified phone number, and set TFA send method to text." #~ msgid "Tag label already in use, please choose another and try again." #~ msgstr "Tag label already in use, please choose another and try again." -#: src/tags/mutations/create-tag.js:97 -#: src/tags/mutations/create-tag.js:154 -#: src/tags/mutations/update-tag.js:183 +#: src/tags/mutations/create-tag.js:94 +#: src/tags/mutations/create-tag.js:148 +#: src/tags/mutations/update-tag.js:161 msgid "Tag label already in use. Please try again with a different label." msgstr "Tag label already in use. Please try again with a different label." @@ -758,15 +764,14 @@ msgstr "Unable leave organization. Please try again." msgid "Unable to add domains in unknown organization." msgstr "Unable to add domains in unknown organization." -#: src/organization/mutations/archive-organization.js:104 -#: src/organization/mutations/archive-organization.js:115 -#: src/organization/mutations/archive-organization.js:134 -#: src/organization/mutations/archive-organization.js:152 -#: src/organization/mutations/archive-organization.js:162 +#: src/organization/data-source.js:179 +#: src/organization/data-source.js:194 +#: src/organization/data-source.js:209 +#: src/organization/data-source.js:217 msgid "Unable to archive organization. Please try again." msgstr "Unable to archive organization. Please try again." -#: src/organization/mutations/archive-organization.js:56 +#: src/organization/mutations/archive-organization.js:52 msgid "Unable to archive unknown organization." msgstr "Unable to archive unknown organization." @@ -777,10 +782,12 @@ msgstr "Unable to archive unknown organization." msgid "Unable to authenticate. Please try again." msgstr "Unable to authenticate. Please try again." -#: src/auth/check-permission.js:26 -#: src/auth/check-permission.js:64 -#: src/auth/check-super-admin.js:20 -#: src/auth/check-super-admin.js:30 +#: src/auth/checks/check-permission.js:26 +#: src/auth/checks/check-permission.js:64 +#: src/auth/checks/check-super-admin.js:20 +#: src/auth/checks/check-super-admin.js:30 +#: src/auth/loaders/load-permission-by-org-id.js:27 +#: src/auth/loaders/load-permission-by-org-id.js:73 msgid "Unable to check permission. Please try again." msgstr "Unable to check permission. Please try again." @@ -806,16 +813,16 @@ msgstr "Unable to confirm completion of the tour. Please try again." msgid "Unable to create domain in unknown organization." msgstr "Unable to create domain in unknown organization." -#: src/domain/mutations/create-domain.js:196 +#: src/domain/mutations/create-domain.js:199 msgid "Unable to create domain, organization has already claimed it." msgstr "Unable to create domain, organization has already claimed it." -#: src/domain/mutations/create-domain.js:178 -#: src/domain/mutations/create-domain.js:186 -#: src/domain/mutations/create-domain.js:218 -#: src/domain/mutations/create-domain.js:227 -#: src/domain/mutations/create-domain.js:247 -#: src/domain/mutations/create-domain.js:255 +#: src/domain/mutations/create-domain.js:181 +#: src/domain/mutations/create-domain.js:189 +#: src/domain/mutations/create-domain.js:221 +#: src/domain/mutations/create-domain.js:230 +#: src/domain/mutations/create-domain.js:250 +#: src/domain/mutations/create-domain.js:258 msgid "Unable to create domain. Please try again." msgstr "Unable to create domain. Please try again." @@ -823,22 +830,22 @@ msgstr "Unable to create domain. Please try again." msgid "Unable to create domains. Please try again." msgstr "Unable to create domains. Please try again." -#: src/organization/mutations/create-organization.js:140 -#: src/organization/mutations/create-organization.js:161 -#: src/organization/mutations/create-organization.js:171 +#: src/organization/data-source.js:58 +#: src/organization/data-source.js:76 +#: src/organization/data-source.js:84 msgid "Unable to create organization. Please try again." msgstr "Unable to create organization. Please try again." -#: src/tags/mutations/create-tag.js:125 +#: src/tags/mutations/create-tag.js:119 msgid "Unable to create tag in unknown organization." msgstr "Unable to create tag in unknown organization." -#: src/tags/mutations/create-tag.js:114 +#: src/tags/mutations/create-tag.js:108 msgid "Unable to create tag, tagId already in use." msgstr "Unable to create tag, tagId already in use." -#: src/tags/mutations/create-tag.js:173 -#: src/tags/mutations/create-tag.js:181 +#: src/tags/data-source.js:58 +#: src/tags/data-source.js:65 msgid "Unable to create tag. Please try again." msgstr "Unable to create tag. Please try again." @@ -857,7 +864,7 @@ msgstr "Unable to dismiss message. Please try again." #~ msgid "Unable to dispatch one time scan. Please try again." #~ msgstr "Unable to dispatch one time scan. Please try again." -#: src/organization/objects/organization.js:270 +#: src/organization/objects/organization.js:265 msgid "Unable to export organization. Please try again." msgstr "Unable to export organization. Please try again." @@ -976,12 +983,12 @@ msgstr "Unable to find verified organization(s). Please try again." msgid "Unable to ignore CVE. Please try again." msgstr "Unable to ignore CVE. Please try again." -#: src/affiliation/mutations/invite-user-to-org.js:123 -#: src/affiliation/mutations/invite-user-to-org.js:189 +#: src/affiliation/mutations/invite-user-to-org.js:124 +#: src/affiliation/mutations/invite-user-to-org.js:190 msgid "Unable to invite user to organization. Please try again." msgstr "Unable to invite user to organization. Please try again." -#: src/affiliation/mutations/invite-user-to-org.js:201 +#: src/affiliation/mutations/invite-user-to-org.js:202 msgid "Unable to invite user to organization. User is already affiliated with organization." msgstr "Unable to invite user to organization. User is already affiliated with organization." @@ -989,8 +996,8 @@ msgstr "Unable to invite user to organization. User is already affiliated with o msgid "Unable to invite user to unknown organization." msgstr "Unable to invite user to unknown organization." -#: src/affiliation/mutations/invite-user-to-org.js:231 -#: src/affiliation/mutations/invite-user-to-org.js:252 +#: src/affiliation/mutations/invite-user-to-org.js:232 +#: src/affiliation/mutations/invite-user-to-org.js:253 msgid "Unable to invite user. Please try again." msgstr "Unable to invite user. Please try again." @@ -1012,7 +1019,7 @@ msgstr "Unable to leave undefined organization." msgid "Unable to load additional findings. Please try again." msgstr "Unable to load additional findings. Please try again." -#: src/auth/check-user-belongs-to-org.js:20 +#: src/auth/checks/check-user-belongs-to-org.js:20 msgid "Unable to load affiliation information. Please try again." msgstr "Unable to load affiliation information. Please try again." @@ -1057,8 +1064,8 @@ msgstr "Unable to load DKIM guidance tag(s). Please try again." #~ msgstr "Unable to load DKIM scan(s). Please try again." #: src/summaries/queries/dkim-summary.js:12 -msgid "Unable to load DKIM summary. Please try again." -msgstr "Unable to load DKIM summary. Please try again." +#~ msgid "Unable to load DKIM summary. Please try again." +#~ msgstr "Unable to load DKIM summary. Please try again." #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:141 @@ -1072,8 +1079,8 @@ msgid "Unable to load DMARC guidance tag(s). Please try again." msgstr "Unable to load DMARC guidance tag(s). Please try again." #: src/summaries/queries/dmarc-phase-summary.js:12 -msgid "Unable to load DMARC phase summary. Please try again." -msgstr "Unable to load DMARC phase summary. Please try again." +#~ msgid "Unable to load DMARC phase summary. Please try again." +#~ msgstr "Unable to load DMARC phase summary. Please try again." #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:319 #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:331 @@ -1090,8 +1097,8 @@ msgid "Unable to load DMARC summary data. Please try again." msgstr "Unable to load DMARC summary data. Please try again." #: src/summaries/queries/dmarc-summary.js:12 -msgid "Unable to load DMARC summary. Please try again." -msgstr "Unable to load DMARC summary. Please try again." +#~ msgid "Unable to load DMARC summary. Please try again." +#~ msgstr "Unable to load DMARC summary. Please try again." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:154 #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:164 @@ -1110,9 +1117,9 @@ msgstr "Unable to load domain selector(s). Please try again." msgid "Unable to load domain. Please try again." msgstr "Unable to load domain. Please try again." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:523 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:533 -#: src/domain/loaders/load-domain-connections-by-user-id.js:575 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:402 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:412 +#: src/domain/loaders/load-domain-connections-by-user-id.js:469 #: src/user/loaders/load-my-tracker-by-user-id.js:33 msgid "Unable to load domain(s). Please try again." msgstr "Unable to load domain(s). Please try again." @@ -1140,8 +1147,8 @@ msgstr "Unable to load HTTPS guidance tag(s). Please try again." #~ msgstr "Unable to load HTTPS scan(s). Please try again." #: src/summaries/queries/https-summary.js:13 -msgid "Unable to load HTTPS summary. Please try again." -msgstr "Unable to load HTTPS summary. Please try again." +#~ msgid "Unable to load HTTPS summary. Please try again." +#~ msgstr "Unable to load HTTPS summary. Please try again." #: src/audit-logs/loaders/load-audit-log-by-key.js:19 #: src/audit-logs/loaders/load-audit-log-by-key.js:31 @@ -1153,12 +1160,12 @@ msgid "Unable to load log(s). Please try again." msgstr "Unable to load log(s). Please try again." #: src/summaries/queries/mail-summary.js:12 -msgid "Unable to load mail summary. Please try again." -msgstr "Unable to load mail summary. Please try again." +#~ msgid "Unable to load mail summary. Please try again." +#~ msgstr "Unable to load mail summary. Please try again." #: src/additional-findings/loaders/load-top-25-reports.js:29 #: src/organization/loaders/load-all-organization-domain-statuses.js:164 -#: src/organization/loaders/load-organization-domain-statuses.js:166 +#: src/organization/loaders/load-organization-domain-statuses.js:172 msgid "Unable to load organization domain statuses. Please try again." msgstr "Unable to load organization domain statuses. Please try again." @@ -1172,6 +1179,13 @@ msgstr "Unable to load organization names. Please try again." msgid "Unable to load organization summary data. Please try again." msgstr "Unable to load organization summary data. Please try again." +#: src/organization/data-source.js:117 +#: src/organization/data-source.js:124 +#: src/organization/data-source.js:144 +#: src/organization/data-source.js:152 +msgid "Unable to load organization. Please try again." +msgstr "Unable to load organization. Please try again." + #: src/organization/loaders/load-organization-by-key.js:31 #: src/organization/loaders/load-organization-by-key.js:41 #: src/organization/loaders/load-organization-by-slug.js:34 @@ -1183,8 +1197,10 @@ msgstr "Unable to load organization summary data. Please try again." msgid "Unable to load organization(s). Please try again." msgstr "Unable to load organization(s). Please try again." -#: src/auth/check-org-owner.js:19 -#: src/auth/check-org-owner.js:27 +#: src/auth/checks/check-org-owner.js:19 +#: src/auth/checks/check-org-owner.js:27 +#: src/auth/loaders/load-org-owner-by-org-id.js:23 +#: src/auth/loaders/load-org-owner-by-org-id.js:33 msgid "Unable to load owner information. Please try again." msgstr "Unable to load owner information. Please try again." @@ -1205,8 +1221,8 @@ msgstr "Unable to load SPF guidance tag(s). Please try again." #~ msgstr "Unable to load SPF scan(s). Please try again." #: src/summaries/queries/spf-summary.js:12 -msgid "Unable to load SPF summary. Please try again." -msgstr "Unable to load SPF summary. Please try again." +#~ msgid "Unable to load SPF summary. Please try again." +#~ msgstr "Unable to load SPF summary. Please try again." #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:272 @@ -1219,13 +1235,13 @@ msgstr "Unable to load SSL guidance tag(s). Please try again." #~ msgstr "Unable to load SSL scan(s). Please try again." #: src/summaries/queries/ssl-summary.js:12 -msgid "Unable to load SSL summary. Please try again." -msgstr "Unable to load SSL summary. Please try again." +#~ msgid "Unable to load SSL summary. Please try again." +#~ msgstr "Unable to load SSL summary. Please try again." #: src/summaries/loaders/load-chart-summary-by-key.js:17 #: src/summaries/loaders/load-chart-summary-by-key.js:25 -msgid "Unable to load summary. Please try again." -msgstr "Unable to load summary. Please try again." +#~ msgid "Unable to load summary. Please try again." +#~ msgstr "Unable to load summary. Please try again." #: src/tags/loaders/load-all-tags.js:36 #: src/tags/loaders/load-all-tags.js:44 @@ -1271,8 +1287,8 @@ msgid "Unable to load verified rua domains. Please try again." msgstr "Unable to load verified rua domains. Please try again." #: src/summaries/queries/web-connections-summary.js:12 -msgid "Unable to load web connections summary. Please try again." -msgstr "Unable to load web connections summary. Please try again." +#~ msgid "Unable to load web connections summary. Please try again." +#~ msgstr "Unable to load web connections summary. Please try again." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:169 #: src/web-scan/loaders/load-web-connections-by-domain-id.js:179 @@ -1283,15 +1299,15 @@ msgid "Unable to load web scan(s). Please try again." msgstr "Unable to load web scan(s). Please try again." #: src/summaries/queries/web-summary.js:13 -msgid "Unable to load web summary. Please try again." -msgstr "Unable to load web summary. Please try again." +#~ msgid "Unable to load web summary. Please try again." +#~ msgstr "Unable to load web summary. Please try again." #: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:293 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:437 msgid "Unable to query affiliation(s). Please try again." msgstr "Unable to query affiliation(s). Please try again." -#: src/domain/loaders/load-domain-connections-by-user-id.js:565 +#: src/domain/loaders/load-domain-connections-by-user-id.js:459 #: src/user/loaders/load-my-tracker-by-user-id.js:23 msgid "Unable to query domain(s). Please try again." msgstr "Unable to query domain(s). Please try again." @@ -1344,19 +1360,17 @@ msgstr "Unable to remove domain. Please try again." msgid "Unable to remove domains from unknown organization." msgstr "Unable to remove domains from unknown organization." -#: src/organization/mutations/remove-organization.js:104 -#: src/organization/mutations/remove-organization.js:115 -#: src/organization/mutations/remove-organization.js:146 -#: src/organization/mutations/remove-organization.js:162 -#: src/organization/mutations/remove-organization.js:193 -#: src/organization/mutations/remove-organization.js:204 -#: src/organization/mutations/remove-organization.js:231 -#: src/organization/mutations/remove-organization.js:250 -#: src/organization/mutations/remove-organization.js:268 -#: src/organization/mutations/remove-organization.js:285 -#: src/organization/mutations/remove-organization.js:318 -#: src/organization/mutations/remove-organization.js:383 -#: src/organization/mutations/remove-organization.js:393 +#: src/organization/data-source.js:278 +#: src/organization/data-source.js:307 +#: src/organization/data-source.js:321 +#: src/organization/data-source.js:350 +#: src/organization/data-source.js:374 +#: src/organization/data-source.js:390 +#: src/organization/data-source.js:405 +#: src/organization/data-source.js:419 +#: src/organization/data-source.js:448 +#: src/organization/data-source.js:490 +#: src/organization/data-source.js:498 msgid "Unable to remove organization. Please try again." msgstr "Unable to remove organization. Please try again." @@ -1369,7 +1383,7 @@ msgstr "Unable to remove phone number. Please try again." msgid "Unable to remove unknown domain." msgstr "Unable to remove unknown domain." -#: src/organization/mutations/remove-organization.js:56 +#: src/organization/mutations/remove-organization.js:52 msgid "Unable to remove unknown organization." msgstr "Unable to remove unknown organization." @@ -1392,15 +1406,15 @@ msgstr "Unable to remove user from this organization. Please try again." msgid "Unable to remove user from unknown organization." msgstr "Unable to remove user from unknown organization." -#: src/domain/mutations/request-scan.js:119 +#: src/domain/mutations/request-scan.js:120 msgid "Unable to request a one time scan on a domain that already has a pending scan." msgstr "Unable to request a one time scan on a domain that already has a pending scan." -#: src/domain/mutations/request-scan.js:54 +#: src/domain/mutations/request-scan.js:55 msgid "Unable to request a one time scan on an unknown domain." msgstr "Unable to request a one time scan on an unknown domain." -#: src/domain/mutations/request-scan.js:127 +#: src/domain/mutations/request-scan.js:128 msgid "Unable to request a one time scan. Please try again." msgstr "Unable to request a one time scan. Please try again." @@ -1443,8 +1457,8 @@ msgstr "Unable to request invite. Please try again." msgid "Unable to reset password. Please try again." msgstr "Unable to reset password. Please try again." -#: src/domain/objects/domain.js:282 -#: src/domain/objects/domain.js:317 +#: src/domain/objects/domain.js:274 +#: src/domain/objects/domain.js:309 msgid "Unable to retrieve DMARC report information for: {domain}" msgstr "Unable to retrieve DMARC report information for: {domain}" @@ -1573,7 +1587,7 @@ msgstr "Unable to unfavourite unknown domain." #~ msgid "Unable to unignore CVE. Please try again." #~ msgstr "Unable to unignore CVE. Please try again." -#: src/domain/mutations/update-domain.js:258 +#: src/domain/mutations/update-domain.js:261 msgid "Unable to update domain edge. Please try again." msgstr "Unable to update domain edge. Please try again." @@ -1586,8 +1600,8 @@ msgid "Unable to update domain that does not belong to the given organization." msgstr "Unable to update domain that does not belong to the given organization." #: src/domain/mutations/update-domain.js:168 -#: src/domain/mutations/update-domain.js:215 -#: src/domain/mutations/update-domain.js:269 +#: src/domain/mutations/update-domain.js:218 +#: src/domain/mutations/update-domain.js:272 msgid "Unable to update domain. Please try again." msgstr "Unable to update domain. Please try again." @@ -1602,11 +1616,7 @@ msgstr "Unable to update domains in unknown organization." msgid "Unable to update domains. Please try again." msgstr "Unable to update domains. Please try again." -#: src/organization/mutations/update-organization.js:175 -#: src/organization/mutations/update-organization.js:201 -#: src/organization/mutations/update-organization.js:209 -#: src/organization/mutations/update-organization.js:263 -#: src/organization/mutations/update-organization.js:271 +#: src/organization/data-source.js:101 msgid "Unable to update organization. Please try again." msgstr "Unable to update organization. Please try again." @@ -1644,11 +1654,11 @@ msgstr "Unable to update role: user does not belong to organization." msgid "Unable to update role: user unknown." msgstr "Unable to update role: user unknown." -#: src/tags/mutations/update-tag.js:135 +#: src/tags/mutations/update-tag.js:113 msgid "Unable to update tag in unknown organization." msgstr "Unable to update tag in unknown organization." -#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:103 msgid "Unable to update tag, orgId is invalid." msgstr "Unable to update tag, orgId is invalid." @@ -1656,10 +1666,10 @@ msgstr "Unable to update tag, orgId is invalid." #~ msgid "Unable to update tag, tagId already in use." #~ msgstr "Unable to update tag, tagId already in use." -#: src/tags/mutations/update-tag.js:91 -#: src/tags/mutations/update-tag.js:99 -#: src/tags/mutations/update-tag.js:222 -#: src/tags/mutations/update-tag.js:233 +#: src/tags/data-source.js:30 +#: src/tags/data-source.js:36 +#: src/tags/data-source.js:92 +#: src/tags/data-source.js:101 msgid "Unable to update tag. Please try again." msgstr "Unable to update tag. Please try again." @@ -1667,11 +1677,11 @@ msgstr "Unable to update tag. Please try again." msgid "Unable to update unknown domain." msgstr "Unable to update unknown domain." -#: src/organization/mutations/update-organization.js:141 +#: src/organization/mutations/update-organization.js:137 msgid "Unable to update unknown organization." msgstr "Unable to update unknown organization." -#: src/tags/mutations/update-tag.js:109 +#: src/tags/mutations/update-tag.js:87 msgid "Unable to update unknown tag." msgstr "Unable to update unknown tag." @@ -1707,13 +1717,13 @@ msgstr "Unable to verify if user is a super admin, please try again." msgid "Unable to verify if user is an admin, please try again." msgstr "Unable to verify if user is an admin, please try again." -#: src/organization/mutations/verify-organization.js:106 -#: src/organization/mutations/verify-organization.js:123 -#: src/organization/mutations/verify-organization.js:131 +#: src/organization/data-source.js:237 +#: src/organization/data-source.js:252 +#: src/organization/data-source.js:260 msgid "Unable to verify organization. Please try again." msgstr "Unable to verify organization. Please try again." -#: src/organization/mutations/verify-organization.js:53 +#: src/organization/mutations/verify-organization.js:50 msgid "Unable to verify unknown organization." msgstr "Unable to verify unknown organization." @@ -1734,11 +1744,11 @@ msgstr "User role was updated successfully." msgid "Username not available, please try another." msgstr "Username not available, please try another." -#: src/auth/tfa-required.js:15 +#: src/auth/guards/tfa-required.js:15 msgid "Verification error. Please activate multi-factor authentication to access content." msgstr "Verification error. Please activate multi-factor authentication to access content." -#: src/auth/verified-required.js:15 +#: src/auth/guards/verified-required.js:15 msgid "Verification error. Please verify your account via email to access content." msgstr "Verification error. Please verify your account via email to access content." @@ -1775,8 +1785,8 @@ msgstr "You must provide a `first` or `last` value to properly paginate the `Dma msgid "You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:140 -#: src/domain/loaders/load-domain-connections-by-user-id.js:165 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:141 +#: src/domain/loaders/load-domain-connections-by-user-id.js:166 msgid "You must provide a `first` or `last` value to properly paginate the `Domain` connection." msgstr "You must provide a `first` or `last` value to properly paginate the `Domain` connection." diff --git a/api/src/locale/fr/messages.po b/api/src/locale/fr/messages.po index a4ca00030d..d1672a6160 100644 --- a/api/src/locale/fr/messages.po +++ b/api/src/locale/fr/messages.po @@ -19,8 +19,8 @@ msgstr "" #: src/dmarc-summaries/loaders/load-dmarc-sum-connections-by-user-id.js:202 #: src/dmarc-summaries/loaders/load-full-pass-connections-by-sum-id.js:83 #: src/dmarc-summaries/loaders/load-spf-failure-connections-by-sum-id.js:83 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:177 -#: src/domain/loaders/load-domain-connections-by-user-id.js:204 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:178 +#: src/domain/loaders/load-domain-connections-by-user-id.js:205 #: src/guidance-tag/loaders/load-aggregate-guidance-tags-connections.js:132 #: src/guidance-tag/loaders/load-dkim-guidance-tags-connections.js:136 #: src/guidance-tag/loaders/load-dmarc-guidance-tags-connections.js:136 @@ -66,8 +66,8 @@ msgstr "`{argSet}` sur la connexion `DmarcFailureTable` ne peut être inférieur msgid "`{argSet}` on the `DmarcSummaries` connection cannot be less than zero." msgstr "`{argSet}` sur la connexion `DmarcSummaries` ne peut être inférieur à zéro." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:156 -#: src/domain/loaders/load-domain-connections-by-user-id.js:181 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:157 +#: src/domain/loaders/load-domain-connections-by-user-id.js:182 msgid "`{argSet}` on the `Domain` connection cannot be less than zero." msgstr "`{argSet}` sur la connexion `Domain` ne peut être inférieur à zéro." @@ -123,33 +123,35 @@ msgstr "`{argSet}` sur la connexion `VerifiedDomain` ne peut être inférieur à msgid "`{argSet}` on the `VerifiedOrganization` connection cannot be less than zero." msgstr "`{argSet}` sur la connexion `VerifiedOrganization` ne peut être inférieur à zéro." -#: src/organization/objects/organization.js:245 +#: src/organization/objects/organization.js:240 #: src/organization/queries/get-all-organization-domain-statuses.js:69 msgid "Assess" msgstr "Évaluez" -#: src/auth/check-permission.js:18 -#: src/auth/check-permission.js:57 -#: src/auth/user-required.js:10 -#: src/auth/user-required.js:21 -#: src/auth/user-required.js:28 +#: src/auth/checks/check-permission.js:18 +#: src/auth/checks/check-permission.js:57 +#: src/auth/guards/user-required.js:10 +#: src/auth/guards/user-required.js:21 +#: src/auth/guards/user-required.js:28 +#: src/auth/loaders/load-permission-by-org-id.js:19 +#: src/auth/loaders/load-permission-by-org-id.js:63 msgid "Authentication error. Please sign in." msgstr "Erreur d'authentification. Veuillez vous connecter." -#: src/domain/objects/domain.js:237 +#: src/domain/objects/domain.js:229 msgid "Cannot query additional findings without permission." msgstr "Il n'est pas possible de demander des résultats supplémentaires sans autorisation." -#: src/organization/objects/organization.js:363 +#: src/organization/objects/organization.js:359 msgid "Cannot query affiliations on organization without admin permission or higher." msgstr "Impossible d'interroger les affiliations sur l'organisation sans l'autorisation de l'administrateur ou plus." #: src/audit-logs/loaders/load-audit-logs-by-org-id.js:224 -#: src/audit-logs/queries/find-audit-logs.js:52 +#: src/audit-logs/queries/find-audit-logs.js:53 msgid "Cannot query audit logs on organization without admin permission or higher." msgstr "Impossible d'interroger les journaux d'audit sur l'organisation sans l'autorisation d'administrateur ou plus." -#: src/domain/objects/domain.js:168 +#: src/domain/objects/domain.js:164 msgid "Cannot query dns scan results without permission." msgstr "Impossible d'interroger les résultats de l'analyse DNS sans autorisation." @@ -157,7 +159,7 @@ msgstr "Impossible d'interroger les résultats de l'analyse DNS sans autorisatio msgid "Cannot query domain selectors without permission." msgstr "Impossible d'interroger les sélecteurs de domaine sans autorisation." -#: src/domain/objects/domain.js:214 +#: src/domain/objects/domain.js:206 msgid "Cannot query web scan results without permission." msgstr "Impossible d'interroger les résultats de l'analyse web sans autorisation." @@ -169,7 +171,7 @@ msgstr "CVE est déjà ignoré pour ce domaine." msgid "CVE is not ignored for this domain." msgstr "Le CVE n'est pas ignoré dans ce domaine." -#: src/organization/objects/organization.js:247 +#: src/organization/objects/organization.js:242 #: src/organization/queries/get-all-organization-domain-statuses.js:71 msgid "Deploy" msgstr "msgstr Déployer" @@ -178,13 +180,13 @@ msgstr "msgstr Déployer" msgid "Email already in use." msgstr "Courriel déjà utilisé." -#: src/organization/objects/organization.js:249 +#: src/organization/objects/organization.js:244 #: src/organization/queries/get-all-organization-domain-statuses.js:73 msgid "Enforce" msgstr "Appliquer" -#: src/domain/mutations/request-scan.js:89 -#: src/domain/mutations/request-scan.js:99 +#: src/domain/mutations/request-scan.js:90 +#: src/domain/mutations/request-scan.js:100 msgid "Error while requesting scan. Please try again." msgstr "Erreur lors de la demande d'analyse. Veuillez réessayer." @@ -211,11 +213,11 @@ msgstr "La valeur du jeton est incorrecte. Veuillez demander un nouvel e-mail." msgid "Incorrect username or password. Please try again." msgstr "Le nom d'utilisateur ou le mot de passe est incorrect. Veuillez réessayer." -#: src/auth/verify-jwt.js:15 +#: src/auth/utils/verify-jwt.js:15 msgid "Invalid token, please sign in." msgstr "Jeton invalide, veuillez vous connecter." -#: src/organization/objects/organization.js:251 +#: src/organization/objects/organization.js:246 #: src/organization/queries/get-all-organization-domain-statuses.js:75 msgid "Maintain" msgstr "Maintenir" @@ -241,22 +243,22 @@ msgstr "Aucun domaine vérifié avec le domaine fourni n'a pu être trouvé." msgid "No verified organization with the provided slug could be found." msgstr "Aucune organisation vérifiée avec le slug fourni n'a pu être trouvée." -#: src/organization/mutations/verify-organization.js:81 +#: src/organization/mutations/verify-organization.js:78 msgid "Organization has already been verified." msgstr "L'organisation a déjà été vérifiée." -#: src/organization/mutations/update-organization.js:185 +#: src/organization/mutations/update-organization.js:167 msgid "Organization name already in use, please choose another and try again." msgstr "Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer." -#: src/organization/mutations/create-organization.js:89 +#: src/organization/mutations/create-organization.js:85 msgid "Organization name already in use. Please try again with a different name." msgstr "Le nom de l'organisation est déjà utilisé. Veuillez réessayer avec un nom différent." -#: src/auth/check-domain-ownership.js:29 -#: src/auth/check-domain-ownership.js:39 -#: src/auth/check-domain-ownership.js:65 -#: src/auth/check-domain-ownership.js:74 +#: src/auth/checks/check-domain-ownership.js:29 +#: src/auth/checks/check-domain-ownership.js:39 +#: src/auth/checks/check-domain-ownership.js:65 +#: src/auth/checks/check-domain-ownership.js:74 msgid "Ownership check error. Unable to request domain information." msgstr "Erreur de vérification de la propriété. Impossible de demander des informations sur le domaine." @@ -290,8 +292,8 @@ msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DmarcFail msgid "Passing both `first` and `last` to paginate the `DmarcSummaries` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `DmarcSummaries` n'est pas supporté." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:147 -#: src/domain/loaders/load-domain-connections-by-user-id.js:172 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:148 +#: src/domain/loaders/load-domain-connections-by-user-id.js:173 msgid "Passing both `first` and `last` to paginate the `Domain` connection is not supported." msgstr "Passer à la fois `first` et `last` pour paginer la connexion `Domain` n'est pas supporté." @@ -364,13 +366,17 @@ msgstr "Le mot de passe a été mis à jour avec succès." msgid "Passwords do not match." msgstr "Les mots de passe ne correspondent pas." -#: src/auth/check-domain-permission.js:22 -#: src/auth/check-domain-permission.js:51 -#: src/auth/check-domain-permission.js:61 +#: src/auth/checks/check-domain-permission.js:22 +#: src/auth/checks/check-domain-permission.js:51 +#: src/auth/checks/check-domain-permission.js:61 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:19 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:51 +#: src/auth/loaders/load-domain-permission-by-domain-id.js:61 msgid "Permission check error. Unable to request domain information." msgstr "Erreur de vérification des permissions. Impossible de demander des informations sur le domaine." #: src/organization/queries/find-organization-by-slug.js:50 +#: src/organization/queries/find-organization-by-slug.js:52 msgid "Permission Denied: Could not retrieve specified organization." msgstr "Permission refusée : Impossible de récupérer l'organisation spécifiée." @@ -386,7 +392,7 @@ msgstr "Permission refusée : Veuillez contacter le propriétaire de l'org pour msgid "Permission Denied: Please contact organization admin for help with archiving domains." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur l'archivage des domaines." -#: src/tags/mutations/create-tag.js:137 +#: src/tags/mutations/create-tag.js:131 msgid "Permission Denied: Please contact organization admin for help with creating tag." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la création d'un tag." @@ -398,7 +404,7 @@ msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisat msgid "Permission Denied: Please contact organization admin for help with removing domains." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des domaines." -#: src/organization/mutations/remove-organization.js:71 +#: src/organization/mutations/remove-organization.js:67 msgid "Permission Denied: Please contact organization admin for help with removing organization." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide afin de supprimer l'organisation." @@ -412,12 +418,12 @@ msgstr "Autorisation refusée : Veuillez contacter l'administrateur de l'organis msgid "Permission Denied: Please contact organization admin for help with updating domains." msgstr "Autorisation refusée : veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide concernant la mise à jour des domaines." -#: src/organization/mutations/update-organization.js:156 +#: src/organization/mutations/update-organization.js:152 msgid "Permission Denied: Please contact organization admin for help with updating organization." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs." -#: src/tags/mutations/update-tag.js:147 -#: src/tags/mutations/update-tag.js:158 +#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:136 msgid "Permission Denied: Please contact organization admin for help with updating tag." msgstr "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la mise à jour de la balise." @@ -448,12 +454,12 @@ msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation #~ msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide afin de récupérer les étiquettes." #: src/domain/queries/find-domain-by-domain.js:51 -#: src/organization/objects/organization.js:199 +#: src/organization/objects/organization.js:195 msgid "Permission Denied: Please contact organization user for help with retrieving this domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide pour récupérer ce domaine." #: src/domain/mutations/request-discovery.js:98 -#: src/domain/mutations/request-scan.js:65 +#: src/domain/mutations/request-scan.js:66 msgid "Permission Denied: Please contact organization user for help with scanning this domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur l'analyse de ce domaine." @@ -461,7 +467,7 @@ msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation msgid "Permission Denied: Please contact organization user for help with updating this domain." msgstr "Autorisation refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine." -#: src/organization/mutations/archive-organization.js:70 +#: src/organization/mutations/archive-organization.js:66 msgid "Permission Denied: Please contact super admin for help with archiving organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'organisation de l'archivage." @@ -470,7 +476,7 @@ msgstr "Permission refusée : Veuillez contacter le super administrateur pour ob msgid "Permission Denied: Please contact super admin for help with removing domain." msgstr "Permission refusée : Veuillez contacter l'utilisateur de l'organisation pour obtenir de l'aide sur la mise à jour de ce domaine." -#: src/organization/mutations/remove-organization.js:84 +#: src/organization/mutations/remove-organization.js:80 msgid "Permission Denied: Please contact super admin for help with removing organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à supprimer l'organisation." @@ -478,11 +484,11 @@ msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu #~ msgid "Permission Denied: Please contact super admin for help with scanning this domain." #~ msgstr "Permission refusée : Veuillez contacter le super administrateur pour obtenir de l'aide sur l'analyse de ce domaine." -#: src/tags/mutations/update-tag.js:169 +#: src/tags/mutations/update-tag.js:147 msgid "Permission Denied: Please contact super admin for help with updating tag." msgstr "Autorisation refusée : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant la mise à jour de la balise." -#: src/affiliation/mutations/invite-user-to-org.js:111 +#: src/affiliation/mutations/invite-user-to-org.js:112 msgid "Permission Denied: Please contact super admin for help with user invitations." msgstr "Accès refusé : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant les invitations d'utilisateurs." @@ -490,14 +496,14 @@ msgstr "Accès refusé : veuillez contacter l'administrateur principal pour obte msgid "Permission Denied: Please contact super admin for help with user role changes." msgstr "Accès refusé : veuillez contacter l'administrateur principal pour obtenir de l'aide concernant la modification des rôles d'utilisateur." -#: src/organization/mutations/verify-organization.js:68 +#: src/organization/mutations/verify-organization.js:65 msgid "Permission Denied: Please contact super admin for help with verifying this organization." msgstr "Permission refusée : Veuillez contacter le super administrateur pour qu'il vous aide à vérifier cette organisation." -#: src/auth/check-user-is-admin-for-user.js:20 -#: src/auth/check-user-is-admin-for-user.js:30 -#: src/auth/check-user-is-admin-for-user.js:63 -#: src/auth/check-user-is-admin-for-user.js:73 +#: src/auth/checks/check-user-is-admin-for-user.js:20 +#: src/auth/checks/check-user-is-admin-for-user.js:30 +#: src/auth/checks/check-user-is-admin-for-user.js:63 +#: src/auth/checks/check-user-is-admin-for-user.js:73 msgid "Permission error, not an admin for this user." msgstr "Erreur de permission, pas d'administrateur pour cet utilisateur." @@ -505,7 +511,7 @@ msgstr "Erreur de permission, pas d'administrateur pour cet utilisateur." msgid "Permission error: Unable to close other user's account." msgstr "Erreur de permission: Impossible de fermer le compte d'un autre utilisateur." -#: src/auth/super-admin-required.js:11 +#: src/auth/guards/super-admin-required.js:11 msgid "Permissions error. You do not have sufficient permissions to access this data." msgstr "Erreur de permissions. Vous n'avez pas les autorisations suffisantes pour accéder à ces données." @@ -567,8 +573,8 @@ msgstr "La demande d'enregistrements `{amount}` sur la connexion `DkimFailureTab msgid "Requesting `{amount}` records on the `DmarcSummaries` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `DmarcSummaries` dépasse la limite `{argSet}` de 100 enregistrements." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:163 -#: src/domain/loaders/load-domain-connections-by-user-id.js:190 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:164 +#: src/domain/loaders/load-domain-connections-by-user-id.js:191 msgid "Requesting `{amount}` records on the `Domain` connection exceeds the `{argSet}` limit of 100 records." msgstr "La demande d'enregistrements `{amount}` sur la connexion `Domain` dépasse la limite `{argSet}` de 100 enregistrements." @@ -622,7 +628,7 @@ msgstr "Ajouté avec succès le(s) domaine(s) {domainCount} à {0}." #~ msgstr "Ajouté avec succès les domaines {domainCount} à {0}." #. placeholder {0}: organization.slug -#: src/organization/mutations/archive-organization.js:196 +#: src/organization/mutations/archive-organization.js:100 msgid "Successfully archived organization: {0}." msgstr "Organisation archivée avec succès : {0}." @@ -631,7 +637,7 @@ msgstr "Organisation archivée avec succès : {0}." msgid "Successfully closed account." msgstr "Le compte a été fermé avec succès." -#: src/domain/mutations/request-scan.js:174 +#: src/domain/mutations/request-scan.js:175 msgid "Successfully dispatched one time scan." msgstr "Un seul balayage a été effectué avec succès." @@ -647,7 +653,7 @@ msgstr "L'analyse de découverte du sous-domaine a été effectuée avec succès msgid "Successfully email verified account." msgstr "Envoi d'un courriel à un compte vérifié." -#: src/affiliation/mutations/invite-user-to-org.js:281 +#: src/affiliation/mutations/invite-user-to-org.js:282 msgid "Successfully invited user to organization, and sent notification email." msgstr "L'utilisateur a été invité avec succès à l'organisation et l'email de notification a été envoyé." @@ -677,7 +683,7 @@ msgid "Successfully removed domain: {0} from favourites." msgstr "A réussi à supprimer le domaine : {0} des favoris." #. placeholder {0}: organization.slug -#: src/organization/mutations/remove-organization.js:419 +#: src/organization/mutations/remove-organization.js:107 msgid "Successfully removed organization: {0}." msgstr "A réussi à supprimer l'organisation : {0}." @@ -689,7 +695,7 @@ msgstr "L'utilisateur a été retiré de l'organisation avec succès." msgid "Successfully requested invite to organization, and sent notification email." msgstr "La demande d'invitation à l'organisation a été effectuée avec succès et un courriel de notification a été envoyé." -#: src/affiliation/mutations/invite-user-to-org.js:169 +#: src/affiliation/mutations/invite-user-to-org.js:170 msgid "Successfully sent invitation to service, and organization email." msgstr "Envoi réussi de l'invitation au service, et de l'email de l'organisation." @@ -711,7 +717,7 @@ msgid "Successfully updated {domainCount} domain(s) in {0} with {1}." msgstr "Mise à jour réussie de {domainCount} domaine(s) dans {0} avec {1}." #. placeholder {0}: currentOrg.slug -#: src/organization/mutations/verify-organization.js:138 +#: src/organization/mutations/verify-organization.js:90 msgid "Successfully verified organization: {0}." msgstr "Envoi réussi de l'invitation au service, et de l'email de l'organisation." @@ -723,9 +729,9 @@ msgstr "Le numéro de téléphone a été vérifié avec succès, et la méthode #~ msgid "Tag label already in use, please choose another and try again." #~ msgstr "L'étiquette est déjà utilisée, veuillez en choisir une autre et réessayer." -#: src/tags/mutations/create-tag.js:97 -#: src/tags/mutations/create-tag.js:154 -#: src/tags/mutations/update-tag.js:183 +#: src/tags/mutations/create-tag.js:94 +#: src/tags/mutations/create-tag.js:148 +#: src/tags/mutations/update-tag.js:161 msgid "Tag label already in use. Please try again with a different label." msgstr "L'étiquette est déjà utilisée. Veuillez réessayer avec une autre étiquette." @@ -758,15 +764,14 @@ msgstr "Impossible de quitter l'organisation. Veuillez réessayer." msgid "Unable to add domains in unknown organization." msgstr "Impossible d'ajouter des domaines dans une organisation inconnue." -#: src/organization/mutations/archive-organization.js:104 -#: src/organization/mutations/archive-organization.js:115 -#: src/organization/mutations/archive-organization.js:134 -#: src/organization/mutations/archive-organization.js:152 -#: src/organization/mutations/archive-organization.js:162 +#: src/organization/data-source.js:179 +#: src/organization/data-source.js:194 +#: src/organization/data-source.js:209 +#: src/organization/data-source.js:217 msgid "Unable to archive organization. Please try again." msgstr "Impossible d'archiver l'organisation. Veuillez réessayer." -#: src/organization/mutations/archive-organization.js:56 +#: src/organization/mutations/archive-organization.js:52 msgid "Unable to archive unknown organization." msgstr "Impossible d'archiver une organisation inconnue." @@ -777,10 +782,12 @@ msgstr "Impossible d'archiver une organisation inconnue." msgid "Unable to authenticate. Please try again." msgstr "Impossible de s'authentifier. Veuillez réessayer." -#: src/auth/check-permission.js:26 -#: src/auth/check-permission.js:64 -#: src/auth/check-super-admin.js:20 -#: src/auth/check-super-admin.js:30 +#: src/auth/checks/check-permission.js:26 +#: src/auth/checks/check-permission.js:64 +#: src/auth/checks/check-super-admin.js:20 +#: src/auth/checks/check-super-admin.js:30 +#: src/auth/loaders/load-permission-by-org-id.js:27 +#: src/auth/loaders/load-permission-by-org-id.js:73 msgid "Unable to check permission. Please try again." msgstr "Impossible de vérifier l'autorisation. Veuillez réessayer." @@ -806,16 +813,16 @@ msgstr "Impossible de confirmer l'achèvement de la visite. Veuillez réessayer. msgid "Unable to create domain in unknown organization." msgstr "Impossible de créer un domaine dans une organisation inconnue." -#: src/domain/mutations/create-domain.js:196 +#: src/domain/mutations/create-domain.js:199 msgid "Unable to create domain, organization has already claimed it." msgstr "Impossible de créer le domaine, l'organisation l'a déjà réclamé." -#: src/domain/mutations/create-domain.js:178 -#: src/domain/mutations/create-domain.js:186 -#: src/domain/mutations/create-domain.js:218 -#: src/domain/mutations/create-domain.js:227 -#: src/domain/mutations/create-domain.js:247 -#: src/domain/mutations/create-domain.js:255 +#: src/domain/mutations/create-domain.js:181 +#: src/domain/mutations/create-domain.js:189 +#: src/domain/mutations/create-domain.js:221 +#: src/domain/mutations/create-domain.js:230 +#: src/domain/mutations/create-domain.js:250 +#: src/domain/mutations/create-domain.js:258 msgid "Unable to create domain. Please try again." msgstr "Impossible de créer un domaine. Veuillez réessayer." @@ -823,22 +830,22 @@ msgstr "Impossible de créer un domaine. Veuillez réessayer." msgid "Unable to create domains. Please try again." msgstr "Impossible de créer des domaines. Veuillez réessayer." -#: src/organization/mutations/create-organization.js:140 -#: src/organization/mutations/create-organization.js:161 -#: src/organization/mutations/create-organization.js:171 +#: src/organization/data-source.js:58 +#: src/organization/data-source.js:76 +#: src/organization/data-source.js:84 msgid "Unable to create organization. Please try again." msgstr "Impossible de créer une organisation. Veuillez réessayer." -#: src/tags/mutations/create-tag.js:125 +#: src/tags/mutations/create-tag.js:119 msgid "Unable to create tag in unknown organization." msgstr "Impossible de créer une étiquette dans une organisation inconnue." -#: src/tags/mutations/create-tag.js:114 +#: src/tags/mutations/create-tag.js:108 msgid "Unable to create tag, tagId already in use." msgstr "Impossible de créer une balise, tagId déjà utilisé." -#: src/tags/mutations/create-tag.js:173 -#: src/tags/mutations/create-tag.js:181 +#: src/tags/data-source.js:58 +#: src/tags/data-source.js:65 msgid "Unable to create tag. Please try again." msgstr "Impossible de créer une balise. Veuillez réessayer." @@ -857,7 +864,7 @@ msgstr "Impossible de rejeter le message. Veuillez réessayer." #~ msgid "Unable to dispatch one time scan. Please try again." #~ msgstr "Impossible d'envoyer un scan unique. Veuillez réessayer." -#: src/organization/objects/organization.js:270 +#: src/organization/objects/organization.js:265 msgid "Unable to export organization. Please try again." msgstr "Impossible d'exporter l'organisation. Veuillez réessayer." @@ -976,12 +983,12 @@ msgstr "Impossible de trouver une ou plusieurs organisations vérifiées. Veuill msgid "Unable to ignore CVE. Please try again." msgstr "Impossible d'ignorer le CVE. Veuillez réessayer." -#: src/affiliation/mutations/invite-user-to-org.js:123 -#: src/affiliation/mutations/invite-user-to-org.js:189 +#: src/affiliation/mutations/invite-user-to-org.js:124 +#: src/affiliation/mutations/invite-user-to-org.js:190 msgid "Unable to invite user to organization. Please try again." msgstr "Impossible d'inviter un utilisateur dans une organisation. Veuillez réessayer." -#: src/affiliation/mutations/invite-user-to-org.js:201 +#: src/affiliation/mutations/invite-user-to-org.js:202 msgid "Unable to invite user to organization. User is already affiliated with organization." msgstr "Impossible d'inviter un utilisateur dans une organisation. L'utilisateur est déjà affilié à l'organisation." @@ -989,8 +996,8 @@ msgstr "Impossible d'inviter un utilisateur dans une organisation. L'utilisateur msgid "Unable to invite user to unknown organization." msgstr "Impossible d'inviter un utilisateur à une organisation inconnue." -#: src/affiliation/mutations/invite-user-to-org.js:231 -#: src/affiliation/mutations/invite-user-to-org.js:252 +#: src/affiliation/mutations/invite-user-to-org.js:232 +#: src/affiliation/mutations/invite-user-to-org.js:253 msgid "Unable to invite user. Please try again." msgstr "Impossible d'inviter un utilisateur. Veuillez réessayer." @@ -1012,7 +1019,7 @@ msgstr "Impossible de quitter une organisation non définie." msgid "Unable to load additional findings. Please try again." msgstr "Impossible de charger des résultats supplémentaires. Veuillez réessayer." -#: src/auth/check-user-belongs-to-org.js:20 +#: src/auth/checks/check-user-belongs-to-org.js:20 msgid "Unable to load affiliation information. Please try again." msgstr "Impossible de charger les informations d'affiliation. Veuillez réessayer." @@ -1057,8 +1064,8 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation DKIM. Veuillez réessay #~ msgstr "Impossible de charger le(s) scan(s) DKIM. Veuillez réessayer." #: src/summaries/queries/dkim-summary.js:12 -msgid "Unable to load DKIM summary. Please try again." -msgstr "Impossible de charger le résumé DKIM. Veuillez réessayer." +#~ msgid "Unable to load DKIM summary. Please try again." +#~ msgstr "Impossible de charger le résumé DKIM. Veuillez réessayer." #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:13 #: src/dmarc-summaries/loaders/load-dmarc-failure-connections-by-sum-id.js:141 @@ -1072,8 +1079,8 @@ msgid "Unable to load DMARC guidance tag(s). Please try again." msgstr "Impossible de charger le(s) tag(s) d'orientation DMARC. Veuillez réessayer." #: src/summaries/queries/dmarc-phase-summary.js:12 -msgid "Unable to load DMARC phase summary. Please try again." -msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." +#~ msgid "Unable to load DMARC phase summary. Please try again." +#~ msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:319 #: src/email-scan/loaders/load-dmarc-connections-by-domain-id.js:331 @@ -1090,8 +1097,8 @@ msgid "Unable to load DMARC summary data. Please try again." msgstr "Impossible de charger les données de synthèse DMARC. Veuillez réessayer." #: src/summaries/queries/dmarc-summary.js:12 -msgid "Unable to load DMARC summary. Please try again." -msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." +#~ msgid "Unable to load DMARC summary. Please try again." +#~ msgstr "Impossible de charger le résumé DMARC. Veuillez réessayer." #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:154 #: src/dns-scan/loaders/load-dns-connections-by-domain-id.js:164 @@ -1110,9 +1117,9 @@ msgstr "Impossible de charger le(s) sélecteur(s) de domaine. Veuillez réessaye msgid "Unable to load domain. Please try again." msgstr "Impossible de charger le domaine. Veuillez réessayer." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:523 -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:533 -#: src/domain/loaders/load-domain-connections-by-user-id.js:575 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:402 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:412 +#: src/domain/loaders/load-domain-connections-by-user-id.js:469 #: src/user/loaders/load-my-tracker-by-user-id.js:33 msgid "Unable to load domain(s). Please try again." msgstr "Impossible de charger le(s) domaine(s). Veuillez réessayer." @@ -1140,8 +1147,8 @@ msgstr "Impossible de charger la ou les balises d'orientation HTTPS. Veuillez r #~ msgstr "Impossible de charger le(s) scan(s) HTTPS. Veuillez réessayer." #: src/summaries/queries/https-summary.js:13 -msgid "Unable to load HTTPS summary. Please try again." -msgstr "Impossible de charger le résumé HTTPS. Veuillez réessayer." +#~ msgid "Unable to load HTTPS summary. Please try again." +#~ msgstr "Impossible de charger le résumé HTTPS. Veuillez réessayer." #: src/audit-logs/loaders/load-audit-log-by-key.js:19 #: src/audit-logs/loaders/load-audit-log-by-key.js:31 @@ -1153,12 +1160,12 @@ msgid "Unable to load log(s). Please try again." msgstr "Impossible de charger le(s) journal(s). Veuillez réessayer." #: src/summaries/queries/mail-summary.js:12 -msgid "Unable to load mail summary. Please try again." -msgstr "Impossible de charger le résumé du courrier. Veuillez réessayer." +#~ msgid "Unable to load mail summary. Please try again." +#~ msgstr "Impossible de charger le résumé du courrier. Veuillez réessayer." #: src/additional-findings/loaders/load-top-25-reports.js:29 #: src/organization/loaders/load-all-organization-domain-statuses.js:164 -#: src/organization/loaders/load-organization-domain-statuses.js:166 +#: src/organization/loaders/load-organization-domain-statuses.js:172 msgid "Unable to load organization domain statuses. Please try again." msgstr "Impossible de charger les statuts des domaines d'organisation. Veuillez réessayer." @@ -1172,6 +1179,13 @@ msgstr "Impossible de charger les noms des organisations. Veuillez réessayer." msgid "Unable to load organization summary data. Please try again." msgstr "Impossible de charger les données de synthèse de l'organisation. Veuillez réessayer." +#: src/organization/data-source.js:117 +#: src/organization/data-source.js:124 +#: src/organization/data-source.js:144 +#: src/organization/data-source.js:152 +msgid "Unable to load organization. Please try again." +msgstr "Impossible de charger l'organisation. Veuillez réessayer." + #: src/organization/loaders/load-organization-by-key.js:31 #: src/organization/loaders/load-organization-by-key.js:41 #: src/organization/loaders/load-organization-by-slug.js:34 @@ -1183,8 +1197,10 @@ msgstr "Impossible de charger les données de synthèse de l'organisation. Veuil msgid "Unable to load organization(s). Please try again." msgstr "Impossible de charger l'organisation (s). Veuillez réessayer." -#: src/auth/check-org-owner.js:19 -#: src/auth/check-org-owner.js:27 +#: src/auth/checks/check-org-owner.js:19 +#: src/auth/checks/check-org-owner.js:27 +#: src/auth/loaders/load-org-owner-by-org-id.js:23 +#: src/auth/loaders/load-org-owner-by-org-id.js:33 msgid "Unable to load owner information. Please try again." msgstr "Impossible de charger les informations sur le propriétaire. Veuillez réessayer." @@ -1205,8 +1221,8 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation SPF. Veuillez réessaye #~ msgstr "Impossible de charger le(s) scan(s) SPF. Veuillez réessayer." #: src/summaries/queries/spf-summary.js:12 -msgid "Unable to load SPF summary. Please try again." -msgstr "Impossible de charger le résumé SPF. Veuillez réessayer." +#~ msgid "Unable to load SPF summary. Please try again." +#~ msgstr "Impossible de charger le résumé SPF. Veuillez réessayer." #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:260 #: src/guidance-tag/loaders/load-ssl-guidance-tags-connections.js:272 @@ -1219,13 +1235,13 @@ msgstr "Impossible de charger le(s) tag(s) d'orientation SSL. Veuillez réessaye #~ msgstr "Impossible de charger le(s) scan(s) SSL. Veuillez réessayer." #: src/summaries/queries/ssl-summary.js:12 -msgid "Unable to load SSL summary. Please try again." -msgstr "Impossible de charger le résumé SSL. Veuillez réessayer." +#~ msgid "Unable to load SSL summary. Please try again." +#~ msgstr "Impossible de charger le résumé SSL. Veuillez réessayer." #: src/summaries/loaders/load-chart-summary-by-key.js:17 #: src/summaries/loaders/load-chart-summary-by-key.js:25 -msgid "Unable to load summary. Please try again." -msgstr "Impossible de charger le résumé. Veuillez réessayer." +#~ msgid "Unable to load summary. Please try again." +#~ msgstr "Impossible de charger le résumé. Veuillez réessayer." #: src/tags/loaders/load-all-tags.js:36 #: src/tags/loaders/load-all-tags.js:44 @@ -1271,8 +1287,8 @@ msgid "Unable to load verified rua domains. Please try again." msgstr "Impossible de charger les domaines rua vérifiés. Veuillez réessayer." #: src/summaries/queries/web-connections-summary.js:12 -msgid "Unable to load web connections summary. Please try again." -msgstr "Impossible de charger le résumé des connexions web. Veuillez réessayer." +#~ msgid "Unable to load web connections summary. Please try again." +#~ msgstr "Impossible de charger le résumé des connexions web. Veuillez réessayer." #: src/web-scan/loaders/load-web-connections-by-domain-id.js:169 #: src/web-scan/loaders/load-web-connections-by-domain-id.js:179 @@ -1283,15 +1299,15 @@ msgid "Unable to load web scan(s). Please try again." msgstr "Impossible de charger le(s) scan(s) web. Veuillez réessayer." #: src/summaries/queries/web-summary.js:13 -msgid "Unable to load web summary. Please try again." -msgstr "Impossible de charger le résumé web. Veuillez réessayer." +#~ msgid "Unable to load web summary. Please try again." +#~ msgstr "Impossible de charger le résumé web. Veuillez réessayer." #: src/affiliation/loaders/load-affiliation-connections-by-org-id.js:293 #: src/affiliation/loaders/load-affiliation-connections-by-user-id.js:437 msgid "Unable to query affiliation(s). Please try again." msgstr "Impossible de demander l'affiliation (s). Veuillez réessayer." -#: src/domain/loaders/load-domain-connections-by-user-id.js:565 +#: src/domain/loaders/load-domain-connections-by-user-id.js:459 #: src/user/loaders/load-my-tracker-by-user-id.js:23 msgid "Unable to query domain(s). Please try again." msgstr "Impossible d'interroger le(s) domaine(s). Veuillez réessayer." @@ -1344,19 +1360,17 @@ msgstr "Impossible de supprimer le domaine. Veuillez réessayer." msgid "Unable to remove domains from unknown organization." msgstr "Impossible de supprimer les domaines d'une organisation inconnue." -#: src/organization/mutations/remove-organization.js:104 -#: src/organization/mutations/remove-organization.js:115 -#: src/organization/mutations/remove-organization.js:146 -#: src/organization/mutations/remove-organization.js:162 -#: src/organization/mutations/remove-organization.js:193 -#: src/organization/mutations/remove-organization.js:204 -#: src/organization/mutations/remove-organization.js:231 -#: src/organization/mutations/remove-organization.js:250 -#: src/organization/mutations/remove-organization.js:268 -#: src/organization/mutations/remove-organization.js:285 -#: src/organization/mutations/remove-organization.js:318 -#: src/organization/mutations/remove-organization.js:383 -#: src/organization/mutations/remove-organization.js:393 +#: src/organization/data-source.js:278 +#: src/organization/data-source.js:307 +#: src/organization/data-source.js:321 +#: src/organization/data-source.js:350 +#: src/organization/data-source.js:374 +#: src/organization/data-source.js:390 +#: src/organization/data-source.js:405 +#: src/organization/data-source.js:419 +#: src/organization/data-source.js:448 +#: src/organization/data-source.js:490 +#: src/organization/data-source.js:498 msgid "Unable to remove organization. Please try again." msgstr "Impossible de supprimer l'organisation. Veuillez réessayer." @@ -1369,7 +1383,7 @@ msgstr "Impossible de supprimer le numéro de téléphone. Veuillez réessayer." msgid "Unable to remove unknown domain." msgstr "Impossible de supprimer un domaine inconnu." -#: src/organization/mutations/remove-organization.js:56 +#: src/organization/mutations/remove-organization.js:52 msgid "Unable to remove unknown organization." msgstr "Impossible de supprimer une organisation inconnue." @@ -1392,15 +1406,15 @@ msgstr "Impossible de supprimer l'utilisateur de cette organisation. Veuillez r msgid "Unable to remove user from unknown organization." msgstr "Impossible de supprimer un utilisateur d'une organisation inconnue." -#: src/domain/mutations/request-scan.js:119 +#: src/domain/mutations/request-scan.js:120 msgid "Unable to request a one time scan on a domain that already has a pending scan." msgstr "Impossible de demander une analyse unique sur un domaine qui a déjà une analyse en cours." -#: src/domain/mutations/request-scan.js:54 +#: src/domain/mutations/request-scan.js:55 msgid "Unable to request a one time scan on an unknown domain." msgstr "Impossible de demander un scan unique sur un domaine inconnu." -#: src/domain/mutations/request-scan.js:127 +#: src/domain/mutations/request-scan.js:128 msgid "Unable to request a one time scan. Please try again." msgstr "Impossible de demander une analyse unique. Veuillez réessayer." @@ -1443,8 +1457,8 @@ msgstr "Impossible de demander une invitation. Veuillez réessayer." msgid "Unable to reset password. Please try again." msgstr "Impossible de réinitialiser le mot de passe. Veuillez réessayer." -#: src/domain/objects/domain.js:282 -#: src/domain/objects/domain.js:317 +#: src/domain/objects/domain.js:274 +#: src/domain/objects/domain.js:309 msgid "Unable to retrieve DMARC report information for: {domain}" msgstr "Impossible de récupérer les informations du rapport DMARC pour : {domain}" @@ -1567,7 +1581,7 @@ msgstr "Impossible de défavoriser le domaine. Veuillez réessayer." msgid "Unable to unfavourite unknown domain." msgstr "Impossible de défavoriser un domaine inconnu." -#: src/domain/mutations/update-domain.js:258 +#: src/domain/mutations/update-domain.js:261 msgid "Unable to update domain edge. Please try again." msgstr "Impossible de mettre à jour le bord du domaine. Veuillez réessayer." @@ -1580,8 +1594,8 @@ msgid "Unable to update domain that does not belong to the given organization." msgstr "Impossible de mettre à jour un domaine qui n'appartient pas à l'organisation donnée." #: src/domain/mutations/update-domain.js:168 -#: src/domain/mutations/update-domain.js:215 -#: src/domain/mutations/update-domain.js:269 +#: src/domain/mutations/update-domain.js:218 +#: src/domain/mutations/update-domain.js:272 msgid "Unable to update domain. Please try again." msgstr "Impossible de mettre à jour le domaine. Veuillez réessayer." @@ -1596,11 +1610,7 @@ msgstr "Impossible de mettre à jour les domaines dans une organisation inconnue msgid "Unable to update domains. Please try again." msgstr "Impossible de mettre à jour les domaines. Veuillez réessayer." -#: src/organization/mutations/update-organization.js:175 -#: src/organization/mutations/update-organization.js:201 -#: src/organization/mutations/update-organization.js:209 -#: src/organization/mutations/update-organization.js:263 -#: src/organization/mutations/update-organization.js:271 +#: src/organization/data-source.js:101 msgid "Unable to update organization. Please try again." msgstr "Impossible de mettre à jour l'organisation. Veuillez réessayer." @@ -1638,11 +1648,11 @@ msgstr "Impossible de mettre à jour le rôle : l'utilisateur n'appartient pas msgid "Unable to update role: user unknown." msgstr "Impossible de mettre à jour le rôle : utilisateur inconnu." -#: src/tags/mutations/update-tag.js:135 +#: src/tags/mutations/update-tag.js:113 msgid "Unable to update tag in unknown organization." msgstr "Impossible de mettre à jour la balise dans une organisation inconnue." -#: src/tags/mutations/update-tag.js:125 +#: src/tags/mutations/update-tag.js:103 msgid "Unable to update tag, orgId is invalid." msgstr "Impossible de mettre à jour la balise, l'orgId est invalide." @@ -1650,10 +1660,10 @@ msgstr "Impossible de mettre à jour la balise, l'orgId est invalide." #~ msgid "Unable to update tag, tagId already in use." #~ msgstr "Impossible de mettre à jour la balise, l'identifiant de balise est déjà utilisé." -#: src/tags/mutations/update-tag.js:91 -#: src/tags/mutations/update-tag.js:99 -#: src/tags/mutations/update-tag.js:222 -#: src/tags/mutations/update-tag.js:233 +#: src/tags/data-source.js:30 +#: src/tags/data-source.js:36 +#: src/tags/data-source.js:92 +#: src/tags/data-source.js:101 msgid "Unable to update tag. Please try again." msgstr "Impossible de mettre à jour la balise. Veuillez réessayer." @@ -1661,11 +1671,11 @@ msgstr "Impossible de mettre à jour la balise. Veuillez réessayer." msgid "Unable to update unknown domain." msgstr "Impossible de mettre à jour un domaine inconnu." -#: src/organization/mutations/update-organization.js:141 +#: src/organization/mutations/update-organization.js:137 msgid "Unable to update unknown organization." msgstr "Impossible de mettre à jour une organisation inconnue." -#: src/tags/mutations/update-tag.js:109 +#: src/tags/mutations/update-tag.js:87 msgid "Unable to update unknown tag." msgstr "Impossible de mettre à jour une étiquette inconnue." @@ -1701,13 +1711,13 @@ msgstr "Impossible de vérifier si l'utilisateur est un super administrateur, ve msgid "Unable to verify if user is an admin, please try again." msgstr "Impossible de vérifier si l'utilisateur est un administrateur, veuillez réessayer." -#: src/organization/mutations/verify-organization.js:106 -#: src/organization/mutations/verify-organization.js:123 -#: src/organization/mutations/verify-organization.js:131 +#: src/organization/data-source.js:237 +#: src/organization/data-source.js:252 +#: src/organization/data-source.js:260 msgid "Unable to verify organization. Please try again." msgstr "Impossible de vérifier l'organisation. Veuillez réessayer." -#: src/organization/mutations/verify-organization.js:53 +#: src/organization/mutations/verify-organization.js:50 msgid "Unable to verify unknown organization." msgstr "Impossible de vérifier une organisation inconnue." @@ -1728,11 +1738,11 @@ msgstr "Le rôle de l'utilisateur a été mis à jour avec succès." msgid "Username not available, please try another." msgstr "Le nom d'utilisateur n'est pas disponible, veuillez en essayer un autre." -#: src/auth/tfa-required.js:15 +#: src/auth/guards/tfa-required.js:15 msgid "Verification error. Please activate multi-factor authentication to access content." msgstr "Erreur de vérification. Veuillez activer l'authentification multifactorielle pour accéder au contenu." -#: src/auth/verified-required.js:15 +#: src/auth/guards/verified-required.js:15 msgid "Verification error. Please verify your account via email to access content." msgstr "Erreur de vérification. Veuillez vérifier votre compte par e-mail pour accéder au contenu." @@ -1769,8 +1779,8 @@ msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctemen msgid "You must provide a `first` or `last` value to properly paginate the `DmarcSummaries` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `DmarcSummaries`." -#: src/domain/loaders/load-domain-connections-by-organizations-id.js:140 -#: src/domain/loaders/load-domain-connections-by-user-id.js:165 +#: src/domain/loaders/load-domain-connections-by-organizations-id.js:141 +#: src/domain/loaders/load-domain-connections-by-user-id.js:166 msgid "You must provide a `first` or `last` value to properly paginate the `Domain` connection." msgstr "Vous devez fournir une valeur `first` ou `last` pour paginer correctement la connexion `Domain`." diff --git a/api/src/organization/data-source.js b/api/src/organization/data-source.js new file mode 100644 index 0000000000..166622e7fd --- /dev/null +++ b/api/src/organization/data-source.js @@ -0,0 +1,501 @@ +import { t } from '@lingui/macro' + +import { + loadAllOrganizationDomainStatuses, + loadOrgByKey, + loadOrgBySlug, + loadOrgConnectionsByDomainId, + loadOrgConnectionsByUserId, + loadOrganizationDomainStatuses, + loadOrganizationNamesById, + loadOrganizationSummariesByPeriod, +} from './loaders' + +export class OrganizationDataSource { + constructor({ query, userKey, i18n, language, cleanseInput, loginRequiredBool, transaction, collections }) { + this._query = query + this._userKey = userKey + this._i18n = i18n + this._transaction = transaction + this._collections = collections + this.byKey = loadOrgByKey({ query, language, userKey, i18n }) + this.bySlug = loadOrgBySlug({ query, language, userKey, i18n }) + this.connectionsByDomainId = loadOrgConnectionsByDomainId({ query, language, userKey, cleanseInput, i18n, auth: { loginRequiredBool } }) + this.connectionsByUserId = loadOrgConnectionsByUserId({ query, userKey, cleanseInput, language, i18n, auth: { loginRequiredBool } }) + this.summariesByPeriod = loadOrganizationSummariesByPeriod({ query, userKey, cleanseInput, i18n }) + this.namesById = loadOrganizationNamesById({ query, userKey, i18n }) + this.domainStatuses = loadOrganizationDomainStatuses({ query, userKey, i18n }) + this.allDomainStatuses = loadAllOrganizationDomainStatuses({ query, userKey, i18n, language }) + } + + async create({ organizationDetails, userId, language }) { + const trx = await this._transaction(this._collections) + + let cursor + try { + cursor = await trx.step( + () => this._query` + WITH organizations + INSERT ${organizationDetails} INTO organizations + RETURN MERGE( + { + _id: NEW._id, + _key: NEW._key, + _rev: NEW._rev, + _type: "organization", + id: NEW._key, + verified: NEW.verified, + domainCount: 0, + summaries: NEW.summaries + }, + TRANSLATE(${language}, NEW.orgDetails) + ) + `, + ) + } catch (err) { + console.error(`Database error occurred when user: ${this._userKey} was creating new organization: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + const organization = await cursor.next() + + try { + await trx.step( + () => this._query` + WITH affiliations, organizations, users + INSERT { + _from: ${organization._id}, + _to: ${userId}, + permission: "owner", + } INTO affiliations + `, + ) + } catch (err) { + console.error(`Database error occurred when inserting affiliation for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred when committing new organization for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to create organization. Please try again.`)) + } + + return organization + } + + async checkNameInUse({ nameEN, nameFR }) { + let cursor + try { + cursor = await this._query` + WITH organizations + FOR org IN organizations + FILTER (org.orgDetails.en.name == ${nameEN}) OR (org.orgDetails.fr.name == ${nameFR}) + RETURN org + ` + } catch (err) { + console.error(`Database error occurred during name check for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to update organization. Please try again.`)) + } + return cursor + } + + async getRawByKey({ orgKey }) { + let cursor + try { + cursor = await this._query` + WITH organizations + FOR org IN organizations + FILTER org._key == ${orgKey} + RETURN org + ` + } catch (err) { + console.error(`Database error occurred while retrieving org: ${orgKey} for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + + try { + return await cursor.next() + } catch (err) { + console.error(`Cursor error occurred while retrieving org: ${orgKey} for user: ${this._userKey}: ${err}`) + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + } + + async update({ orgKey, updatedOrgDetails }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH organizations + UPSERT { _key: ${orgKey} } + INSERT ${updatedOrgDetails} + UPDATE ${updatedOrgDetails} + IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred while upserting org: ${orgKey} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred while committing org: ${orgKey} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to load organization. Please try again.`)) + } + } + + async archive({ organization }) { + const trx = await this._transaction(this._collections) + + let domainInfo + try { + const countCursor = await this._query` + WITH claims, domains, organizations + LET domainIds = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + RETURN e._to + ) + FOR domain IN domains + FILTER domain._id IN domainIds + LET count = LENGTH( + FOR v, e IN 1..1 INBOUND domain._id claims + RETURN 1 + ) + RETURN { _id: domain._id, _key: domain._key, count } + ` + domainInfo = await countCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while gathering domain count for archive of org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + + for (const domain of domainInfo) { + if (domain.count === 1) { + try { + await trx.step( + () => this._query` + WITH domains + UPDATE { _key: ${domain._key}, archived: true } IN domains + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while archiving domains for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + } + } + + try { + await trx.step( + () => this._query` + WITH organizations + UPDATE { _key: ${organization._key}, verified: false } IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while unverifying org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred for user: ${this._userKey} while archiving org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to archive organization. Please try again.`)) + } + } + + async verify({ currentOrg }) { + const trx = await this._transaction(this._collections) + + try { + await trx.step( + () => this._query` + WITH organizations + UPSERT { _key: ${currentOrg._key} } + INSERT ${currentOrg} + UPDATE ${currentOrg} + IN organizations + `, + ) + } catch (err) { + console.error(`Transaction error occurred while upserting verified org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH domains, claims + FOR v, e IN 1..1 OUTBOUND ${currentOrg._id} claims + FILTER v.archived == true + UPDATE v WITH { archived: false } IN domains + `, + ) + } catch (err) { + console.error(`Transaction error occurred while unarchiving affiliated domains for org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction error occurred while committing verified org: ${currentOrg._key} for user: ${this._userKey}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to verify organization. Please try again.`)) + } + } + + async remove({ organization }) { + const trx = await this._transaction(this._collections) + + let dmarcSummaryCheckList + try { + const dmarcSummaryCheckCursor = await this._query` + WITH domains, ownership, dmarcSummaries, organizations + FOR v, e IN 1..1 OUTBOUND ${organization._id} ownership + RETURN e + ` + dmarcSummaryCheckList = await dmarcSummaryCheckCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while getting dmarc summaries for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + for (const ownership of dmarcSummaryCheckList) { + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries + LET dmarcSummaryEdges = ( + FOR v, e IN 1..1 OUTBOUND ${ownership._to} domainsToDmarcSummaries + RETURN { edgeKey: e._key, dmarcSummaryId: e._to } + ) + LET removeDmarcSummaryEdges = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries + OPTIONS { waitForSync: true } + ) + LET removeDmarcSummary = ( + FOR dmarcSummaryEdge IN dmarcSummaryEdges + LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key + REMOVE key IN dmarcSummaries + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing dmarc summaries for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH ownership, organizations, domains + REMOVE ${ownership._key} IN ownership + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing ownerships for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } + + let domainInfo + try { + const countCursor = await this._query` + WITH claims, domains, organizations + LET domainIds = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + RETURN e._to + ) + FOR domain IN domains + FILTER domain._id IN domainIds + LET count = LENGTH( + FOR v, e IN 1..1 INBOUND domain._id claims + RETURN 1 + ) + RETURN { + "_id": domain._id, + "_key": domain._key, + "domain": domain.domain, + "count": count + } + ` + domainInfo = await countCursor.all() + } catch (err) { + console.error(`Database error occurred for user: ${this._userKey} while gathering domain count for removal of org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + for (const domain of domainInfo) { + if (domain.count === 1) { + try { + await trx.step(async () => { + await this._query` + WITH web, webScan + FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb + LET removeWebScansQuery = ( + FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans + REMOVE webScanV IN webScan + REMOVE webToWebScansV IN webToWebScans + OPTIONS { waitForSync: true } + ) + REMOVE webV IN web + REMOVE domainsWebEdge IN domainsWeb + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing web data for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH dns + FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS + REMOVE dnsV IN dns + REMOVE domainsDNSEdge IN domainsDNS + OPTIONS { waitForSync: true } + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing DNS data for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + WITH favourites, domains + FOR fav IN favourites + FILTER fav._to == ${domain._id} + REMOVE fav IN favourites + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing favourites for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step(async () => { + await this._query` + FOR e IN domainsToSelectors + FILTER e._from == ${domain._id} + REMOVE e IN domainsToSelectors + ` + }) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing DKIM selectors for ${domain.domain} in org: ${organization.slug}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.step( + () => this._query` + WITH claims, domains, organizations + LET domainEdges = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} claims + FILTER e._to == ${domain._id} + RETURN { edgeKey: e._key, domainId: e._to } + ) + LET removeDomainEdges = ( + FOR domainEdge in domainEdges + REMOVE domainEdge.edgeKey IN claims + OPTIONS { waitForSync: true } + ) + LET removeDomain = ( + FOR domainEdge in domainEdges + LET key = PARSE_IDENTIFIER(domainEdge.domainId).key + REMOVE key IN domains + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing domains for org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } + } + + try { + await trx.step( + () => this._query` + WITH affiliations, organizations, users + LET userEdges = ( + FOR v, e IN 1..1 OUTBOUND ${organization._id} affiliations + RETURN { edgeKey: e._key, userKey: e._to } + ) + LET removeUserEdges = ( + FOR userEdge IN userEdges + REMOVE userEdge.edgeKey IN affiliations + OPTIONS { waitForSync: true } + ) + RETURN true + `, + ) + + await trx.step( + () => this._query` + WITH organizations, organizationSummaries + FOR summary in organizationSummaries + FILTER summary.organization == ${organization._id} + REMOVE summary._key IN organizationSummaries + OPTIONS { waitForSync: true } + `, + ) + + await trx.step( + () => this._query` + WITH organizations + REMOVE ${organization._key} IN organizations + OPTIONS { waitForSync: true } + `, + ) + } catch (err) { + console.error(`Transaction error occurred for user: ${this._userKey} while removing affiliations and org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + + try { + await trx.commit() + } catch (err) { + console.error(`Transaction commit error occurred for user: ${this._userKey} while removing org: ${organization._key}: ${err}`) + await trx.abort() + throw new Error(this._i18n._(t`Unable to remove organization. Please try again.`)) + } + } +} diff --git a/api/src/organization/index.js b/api/src/organization/index.js index e170187ec3..9366445c50 100644 --- a/api/src/organization/index.js +++ b/api/src/organization/index.js @@ -1,3 +1,4 @@ +export * from './data-source' export * from './inputs' export * from './loaders' export * from './mutations' diff --git a/api/src/organization/mutations/__tests__/archive-organization.test.js b/api/src/organization/mutations/__tests__/archive-organization.test.js index 9e1f992a0a..e574daa6ff 100644 --- a/api/src/organization/mutations/__tests__/archive-organization.test.js +++ b/api/src/organization/mutations/__tests__/archive-organization.test.js @@ -11,7 +11,7 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -176,8 +176,18 @@ describe('archiving an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -242,8 +252,18 @@ describe('archiving an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -315,11 +335,9 @@ describe('archiving an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -383,8 +401,7 @@ describe('archiving an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: true, @@ -411,8 +428,7 @@ describe('archiving an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -437,20 +453,8 @@ describe('archiving an organization', () => { }) }) }) - describe('given a trx commit error', () => { + describe('given a data source error', () => { it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockReturnValue([]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({}), - commit: jest.fn().mockRejectedValue(new Error('Commit Error')), - abort: jest.fn(), - }) - const response = await graphql({ schema, source: ` @@ -478,9 +482,6 @@ describe('archiving an organization', () => { rootValue: null, contextValue: { i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, userKey: 123, request: { ip: '127.0.0.1' }, auth: { @@ -489,34 +490,18 @@ describe('archiving an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), + dataSources: { + organization: { + byKey: { + load: jest.fn().mockReturnValue({ + _id: 'organizations/123', + _key: 123, + verified: false, + slug: 'treasury-board-secretariat', + name: 'Treasury Board of Canada Secretariat', + }), + }, + archive: jest.fn().mockRejectedValue(new Error('Unable to archive organization. Please try again.')), }, }, }, @@ -525,9 +510,7 @@ describe('archiving an organization', () => { const error = [new GraphQLError('Unable to archive organization. Please try again.')] expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx commit error occurred for user: 123 while attempting archive of org: 123, Error: Commit Error`, - ]) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/create-organization.test.js b/api/src/organization/mutations/__tests__/create-organization.test.js index 931046cc14..3ab87077a2 100644 --- a/api/src/organization/mutations/__tests__/create-organization.test.js +++ b/api/src/organization/mutations/__tests__/create-organization.test.js @@ -11,7 +11,7 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput, slugify } from '../../../validators' import { checkSuperAdmin, superAdminRequired, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgBySlug } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -107,6 +107,7 @@ describe('create an organization', () => { contextValue: { request: { language: 'en', + ip: '1.2.3.4', }, query, collections: collectionNames, @@ -121,8 +122,19 @@ describe('create an organization', () => { checkSuperAdmin: checkSuperAdmin({ i18n, query, userKey: user._key }), superAdminRequired: superAdminRequired({ i18n }), }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgBySlug: loadOrgBySlug({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, validators: { @@ -194,6 +206,7 @@ describe('create an organization', () => { contextValue: { request: { language: 'fr', + ip: '1.2.3.4', }, query, collections: collectionNames, @@ -208,8 +221,19 @@ describe('create an organization', () => { checkSuperAdmin: checkSuperAdmin({ i18n, query, userKey: user._key }), superAdminRequired: superAdminRequired({ i18n }), }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'fr', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgBySlug: loadOrgBySlug({ query, language: 'fr' }), loadUserByKey: loadUserByKey({ query }), }, validators: { @@ -301,10 +325,8 @@ describe('create an organization', () => { checkSuperAdmin: jest.fn(), superAdminRequired: jest.fn(), }, + dataSources: { organization: { bySlug: { loadMany: jest.fn().mockReturnValue([{}, undefined]) } } }, loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([{}, undefined]), - }, loadUserByKey: jest.fn(), }, validators: { @@ -331,226 +353,61 @@ describe('create an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when inserting organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } - } + describe('data source error occurs', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + createOrganization( + input: { + acronymEN: "TBS" + acronymFR: "SCT" + nameEN: "Treasury Board of Canada Secretariat" + nameFR: "Secrétariat du Conseil Trésor du Canada" } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - }, - }) - - const error = [new GraphQLError('Unable to create organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when user: 123 was creating new organization treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" + ) { + result { + ... on Organization { + id + acronym + slug + name + verified } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn() }) - .mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, + } + `, + rootValue: null, + contextValue: { + i18n, + request: { language: 'en' }, + userKey: 123, + auth: { + userRequired: jest.fn().mockReturnValue({ _key: 123 }), + verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, - }) - - const error = [new GraphQLError('Unable to create organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when inserting edge definition for user: 123 to treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when committing information to db', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn() }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, + dataSources: { + organization: { + bySlug: { loadMany: jest.fn().mockReturnValue([undefined, undefined]) }, + create: jest.fn().mockRejectedValue(new Error('Unable to create organization. Please try again.')), }, }, - }) + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Unable to create organization. Please try again.')] + const error = [new GraphQLError('Unable to create organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when committing new organization: treasury-board-of-canada-secretariat for user: 123 to db: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) @@ -617,10 +474,8 @@ describe('create an organization', () => { checkSuperAdmin: jest.fn(), superAdminRequired: jest.fn(), }, + dataSources: { organization: { bySlug: { loadMany: jest.fn().mockReturnValue([{}, undefined]) } } }, loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([{}, undefined]), - }, loadUserByKey: jest.fn(), }, validators: { @@ -647,226 +502,61 @@ describe('create an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when inserting organization', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } - } + describe('data source error occurs', () => { + it('returns an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + createOrganization( + input: { + acronymEN: "TBS" + acronymFR: "SCT" + nameEN: "Treasury Board of Canada Secretariat" + nameFR: "Secrétariat du Conseil Trésor du Canada" } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - }, - }) - - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when user: 123 was creating new organization treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when inserting edge', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" + ) { + result { + ... on Organization { + id + acronym + slug + name + verified } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({ next: jest.fn() }) - .mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, + } + `, + rootValue: null, + contextValue: { + i18n, + request: { language: 'en' }, + userKey: 123, + auth: { + userRequired: jest.fn().mockReturnValue({ _key: 123 }), + verifiedRequired: jest.fn(), + checkSuperAdmin: jest.fn(), + superAdminRequired: jest.fn(), }, - }) - - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when inserting edge definition for user: 123 to treasury-board-of-canada-secretariat: Error: trx step error`, - ]) - }) - }) - describe('when committing information to db', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - createOrganization( - input: { - acronymEN: "TBS" - acronymFR: "SCT" - nameEN: "Treasury Board of Canada Secretariat" - nameFR: "Secrétariat du Conseil Trésor du Canada" - } - ) { - result { - ... on Organization { - id - acronym - slug - name - verified - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - request: { - language: 'en', - }, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({ next: jest.fn() }), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - userRequired: jest.fn().mockReturnValue({ - _key: 123, - }), - verifiedRequired: jest.fn(), - checkSuperAdmin: jest.fn(), - superAdminRequired: jest.fn(), - }, - loaders: { - loadOrgBySlug: { - loadMany: jest.fn().mockReturnValue([undefined, undefined]), - }, - loadUserByKey: jest.fn(), - }, - validators: { - cleanseInput, - slugify, + dataSources: { + organization: { + bySlug: { loadMany: jest.fn().mockReturnValue([undefined, undefined]) }, + create: jest.fn().mockRejectedValue(new Error('Impossible de créer une organisation. Veuillez réessayer.')), }, }, - }) + validators: { cleanseInput, slugify }, + }, + }) - const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] + const error = [new GraphQLError('Impossible de créer une organisation. Veuillez réessayer.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred when committing new organization: treasury-board-of-canada-secretariat for user: 123 to db: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/remove-organization.test.js b/api/src/organization/mutations/__tests__/remove-organization.test.js index b0b3641d81..7b179c6d81 100644 --- a/api/src/organization/mutations/__tests__/remove-organization.test.js +++ b/api/src/organization/mutations/__tests__/remove-organization.test.js @@ -11,7 +11,7 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -209,8 +209,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -274,8 +285,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -333,8 +355,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -400,8 +433,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -458,8 +502,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -522,8 +577,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -580,8 +646,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -653,8 +730,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -709,8 +797,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -768,8 +867,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -826,8 +936,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -950,8 +1071,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1015,8 +1147,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1074,8 +1217,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1141,8 +1295,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1197,8 +1362,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1257,8 +1433,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1315,8 +1502,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1388,8 +1586,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1444,8 +1653,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1503,8 +1723,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1561,8 +1792,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1674,8 +1916,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1739,8 +1992,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1798,8 +2062,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1865,8 +2140,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1922,8 +2208,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -1982,8 +2279,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2040,8 +2348,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2113,8 +2432,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2169,8 +2499,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2228,8 +2569,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2286,8 +2638,19 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: new OrganizationDataSource({ + query, + userKey: user._key, + i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, + }), + }, loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), }, }, @@ -2366,11 +2729,9 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -2434,8 +2795,7 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: true, @@ -2462,8 +2822,7 @@ describe('removing an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -2527,8 +2886,7 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _key: 123, verified: false, @@ -2555,8 +2913,7 @@ describe('removing an organization', () => { }, }, }), - }, - }, + } } }, }, }) @@ -2581,961 +2938,8 @@ describe('removing an organization', () => { }) }) }) - describe('given a database error', () => { - describe('when getting the ownership information', () => { - it('throws an error', async () => { - const mockedQuery = jest.fn().mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred for user: 123 while attempting to get dmarcSummaryInfo while removing org: 123, Error: Database Error`, - ]) - }) - }) - describe('when getting the domain claim count', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([]), - } - - const mockedQuery = jest - .fn() - .mockReturnValueOnce(mockedCursor) - .mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred for user: 123 while attempting to gather domain count while removing org: 123, Error: Database Error`, - ]) - }) - }) - }) - describe('given a cursor error', () => { - describe('when getting getting ownership information', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockRejectedValue(new Error('Cursor Error')), - } - - const mockedQuery = jest - .fn() - .mockReturnValueOnce(mockedCursor) - .mockRejectedValue(new Error('Database Error')) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 while attempting to get dmarcSummaryInfo while removing org: 123, Error: Cursor Error`, - ]) - }) - }) - describe('when getting getting domain claim count', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockRejectedValue(new Error('Cursor Error')), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred for user: 123 while attempting to gather domain count while removing org: 123, Error: Cursor Error`, - ]) - }) - }) - }) - describe('given a trx step error', () => { - describe('when removing dmarc summary data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([{}]), - } - - const mockedQuery = jest.fn().mockReturnValueOnce(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove dmarc summaries while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing ownership data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValue([{}]), - } - - const mockedQuery = jest.fn().mockReturnValueOnce(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({}).mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove ownerships while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing web scan results data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1, domain: 'test.gc.ca' }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred while user: 123 attempted to remove web data for test.gc.ca in org: undefined, Error: Trx Step`, - ]) - }) - }) - describe('when removing scan data', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1, domain: 'test.gc.ca' }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValueOnce({}).mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred while user: 123 attempted to remove DNS data for test.gc.ca in org: undefined, error: Error: Trx Step`, - ]) - }) - }) - describe('when removing domain', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1 }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove domains while removing org: 123, Error: Trx Step`, - ]) - }) - }) - describe('when removing affiliations and org', () => { - it('throws an error', async () => { - const mockedCursor = { - all: jest - .fn() - .mockReturnValueOnce([]) - .mockReturnValue([{ count: 1 }]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest - .fn() - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockReturnValueOnce({}) - .mockRejectedValue(new Error('Trx Step')), - abort: jest.fn(), - }) - - const response = await graphql({ - schema, - source: ` - mutation { - removeOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('owner'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to remove organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx step error occurred for user: 123 while attempting to remove affiliations, and the org while removing org: 123, Error: Trx Step`, - ]) - }) - }) - }) - describe('given a trx commit error', () => { + describe('given a data source error', () => { it('throws an error', async () => { - const mockedCursor = { - all: jest.fn().mockReturnValueOnce([]).mockReturnValue([]), - } - - const mockedQuery = jest.fn().mockReturnValue(mockedCursor) - - const mockedTransaction = jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue({}), - commit: jest.fn().mockRejectedValue(new Error('Commit Error')), - abort: jest.fn(), - }) - const response = await graphql({ schema, source: ` @@ -3563,9 +2967,6 @@ describe('removing an organization', () => { rootValue: null, contextValue: { i18n, - query: mockedQuery, - collections: collectionNames, - transaction: mockedTransaction, userKey: 123, request: { ip: '127.0.0.1' }, auth: { @@ -3574,34 +2975,25 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - _key: 123, - verified: false, + dataSources: { + auditLogs: { logActivity: jest.fn() }, + organization: { + byKey: { + load: jest.fn().mockReturnValue({ + _id: 'organizations/123', + _key: '123', + verified: false, + slug: 'treasury-board-secretariat', + name: 'Treasury Board of Canada Secretariat', + }), + }, + getRawByKey: jest.fn().mockResolvedValue({ orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, + en: { name: 'Treasury Board of Canada Secretariat' }, + fr: { name: 'Secrétariat du Conseil Trésor du Canada' }, }, }), + remove: jest.fn().mockRejectedValue(new Error('Unable to remove organization. Please try again.')), }, }, }, @@ -3610,9 +3002,7 @@ describe('removing an organization', () => { const error = [new GraphQLError('Unable to remove organization. Please try again.')] expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Trx commit error occurred for user: 123 while attempting remove of org: 123, Error: Commit Error`, - ]) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/__tests__/update-organization.test.js b/api/src/organization/mutations/__tests__/update-organization.test.js index 48d617201c..1a169220ba 100644 --- a/api/src/organization/mutations/__tests__/update-organization.test.js +++ b/api/src/organization/mutations/__tests__/update-organization.test.js @@ -1,7 +1,5 @@ import { setupI18n } from '@lingui/core' -import { dbNameFromFile } from 'arango-tools' -import { ensureDatabase as ensure } from '../../../testUtilities' -import { graphql, GraphQLSchema, GraphQLError } from 'graphql' +import { graphql, GraphQLSchema } from 'graphql' import { toGlobalId } from 'graphql-relay' import { createQuerySchema } from '../../../query' @@ -9,4165 +7,866 @@ import { createMutationSchema } from '../../../mutation' import englishMessages from '../../../locale/en/messages' import frenchMessages from '../../../locale/fr/messages' import { cleanseInput, slugify } from '../../../validators' -import { checkPermission, userRequired, verifiedRequired } from '../../../auth' -import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' -import dbschema from '../../../../database.json' -import { collectionNames } from '../../../collection-names' - -const { DB_PASS: rootPass, DB_URL: url } = process.env - -describe('updating an organization', () => { - let query, drop, truncate, schema, collections, transaction, user +const ORG_KEY = 'org123' +const ORG_GID = toGlobalId('organization', ORG_KEY) + +const BASE_RAW_ORG = { + _id: `organizations/${ORG_KEY}`, + _key: ORG_KEY, + externalId: 'ext-001', + externallyManaged: false, + orgDetails: { + en: { + slug: 'treasury-board-secretariat', + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + fr: { + slug: 'secretariat-conseil-tresor', + acronym: 'SCT', + name: 'Secrétariat du Conseil Trésor du Canada', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + }, + }, +} + +// Flat object returned by byKey.load after the update (language-resolved, as the Organization type expects). +// _type: 'organization' is required by updateOrganizationUnion resolveType. +const RESOLVED_ORG = { + _type: 'organization', + id: ORG_KEY, + _key: ORG_KEY, + _id: `organizations/${ORG_KEY}`, + acronym: 'TBS', + name: 'Treasury Board of Canada Secretariat', + slug: 'treasury-board-secretariat', + zone: 'FED', + sector: 'TBS', + country: 'Canada', + province: 'Ontario', + city: 'Ottawa', + verified: false, +} + +const BASE_USER = { _key: 'user123', userName: 'test@example.com' } + +describe('updateOrganization', () => { + let schema, enI18n, frI18n const consoleOutput = [] - const mockedInfo = (output) => consoleOutput.push(output) - const mockedWarn = (output) => consoleOutput.push(output) - const mockedError = (output) => consoleOutput.push(output) + beforeAll(() => { - console.info = mockedInfo - console.warn = mockedWarn - console.error = mockedError - // Create GQL Schema + console.info = (o) => consoleOutput.push(o) + console.warn = (o) => consoleOutput.push(o) + console.error = (o) => consoleOutput.push(o) + schema = new GraphQLSchema({ query: createQuerySchema(), mutation: createMutationSchema(), }) + + enI18n = setupI18n({ + locale: 'en', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) + + frI18n = setupI18n({ + locale: 'fr', + localeData: { en: { plurals: {} }, fr: { plurals: {} } }, + locales: ['en', 'fr'], + messages: { en: englishMessages.messages, fr: frenchMessages.messages }, + }) }) - beforeEach(() => { + + afterEach(() => { consoleOutput.length = 0 }) - describe('given a successful organization update', () => { - let org - beforeEach(async () => { - // Generate DB Items - ;({ query, drop, truncate, collections, transaction } = await ensure({ - variables: { - dbname: dbNameFromFile(__filename), - username: 'root', - rootPassword: rootPass, - password: rootPass, - url, - }, - - schema: dbschema, - })) - user = await collections.users.save({ - userName: 'test.account@istio.actually.exists', - emailValidated: true, - }) - org = await collections.organizations.save({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, + // Builds the organization data source mock. firstLoad is what byKey.load returns on the + // initial existence check; secondLoad is what it returns after the update + cache clear. + // Note: uses hasOwnProperty so callers can explicitly pass undefined (org not found). + function makeOrgDS(opts = {}) { + const firstLoad = Object.prototype.hasOwnProperty.call(opts, 'firstLoad') ? opts.firstLoad : BASE_RAW_ORG + const secondLoad = opts.secondLoad ?? RESOLVED_ORG + const nameInUseCount = opts.nameInUseCount ?? 0 + const rawOrg = opts.rawOrg ?? BASE_RAW_ORG + const update = opts.update ?? jest.fn() + const checkNameInUse = opts.checkNameInUse ?? null + const getRawByKey = opts.getRawByKey ?? null + + return { + byKey: { + load: jest.fn().mockResolvedValueOnce(firstLoad).mockResolvedValueOnce(secondLoad), + clear: jest.fn(), + }, + checkNameInUse: checkNameInUse ?? jest.fn().mockResolvedValue({ count: nameInUseCount }), + getRawByKey: getRawByKey ?? jest.fn().mockResolvedValue(rawOrg), + update, + } + } + + function makeContext({ + i18n = enI18n, + permission = 'admin', + orgDS = null, + logActivity = jest.fn(), + userKey = 'user123', + ip = '1.2.3.4', + } = {}) { + return { + i18n, + userKey, + request: { ip }, + auth: { + userRequired: jest.fn().mockReturnValue(BASE_USER), + verifiedRequired: jest.fn(), + checkPermission: jest.fn().mockReturnValue(permission), + }, + dataSources: { + auditLogs: { logActivity }, + organization: orgDS ?? makeOrgDS(), + }, + validators: { cleanseInput, slugify }, + } + } + + describe('given a successful update', () => { + it('returns the updated organization on success', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { + id: "${ORG_GID}" + nameEN: "New English Name" + nameFR: "Nouveau Nom Français" + acronymEN: "NEN" + acronymFR: "NNF" + }) { + result { + ... on Organization { id name acronym slug } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext(), }) - }) - - afterEach(async () => { - await truncate() - await drop() - }) - describe('users permission level is super_admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'super_admin', - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toMatchObject({ + name: RESOLVED_ORG.name, + acronym: RESOLVED_ORG.acronym, + slug: RESOLVED_ORG.slug, }) - describe('users language is english', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(consoleOutput).toContain(`User: user123, successfully updated org ${ORG_KEY}.`) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('calls update with correctly merged org details', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameEN: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Updated Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneEN: "New Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + orgKey: ORG_KEY, + updatedOrgDetails: expect.objectContaining({ + orgDetails: expect.objectContaining({ + en: expect.objectContaining({ name: 'Updated Name' }), + }), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'New Zone', - }, - }, - }, - } + it('preserves existing fields when only partial inputs are provided', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorEN: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Only EN Updated" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'New Sector', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryEN: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails.orgDetails.fr.name).toBe(BASE_RAW_ORG.orgDetails.fr.name) + expect(updatedOrgDetails.orgDetails.en.acronym).toBe(BASE_RAW_ORG.orgDetails.en.acronym) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'A New Country', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('clears byKey cache before reloading the organization', async () => { + const clear = jest.fn() + const orgDS = makeOrgDS() + orgDS.byKey.clear = clear - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceEN: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Test" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'A New Province', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(clear).toHaveBeenCalledWith(ORG_KEY) + }) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'A New City', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + describe('audit logging', () => { + it('logs audit activity when nameEN is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Brand New Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NEW_ACRONYM', - name: 'New Name', - zone: 'New Zone', - sector: 'New Sector', - country: 'New Country', - province: 'New Province', - city: 'New City', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ logActivity }), }) - describe('users language is french', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymFR: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'update', + target: expect.objectContaining({ + resourceType: 'organization', + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ + name: 'nameEN', + oldValue: BASE_RAW_ORG.orgDetails.en.name, + newValue: 'Brand New Name', + }), + ]), + }), + }), + ) + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameFR: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + it('logs audit activity when nameFR is changed', async () => { + const logActivity = jest.fn() - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameFR: "Nouveau Nom" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneFR: "Secret Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'nameFR', newValue: 'Nouveau Nom' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'Secret Zone', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('logs audit activity when acronymEN is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorFR: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" acronymEN: "NEW" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'New Sector', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryFR: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'acronymEN', newValue: 'NEW' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'A New Country', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('logs audit activity when acronymFR is changed', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceFR: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" acronymFR: "NVL" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'A New Province', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityFR: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ + updatedProperties: expect.arrayContaining([ + expect.objectContaining({ name: 'acronymFR', newValue: 'NVL' }), + ]), + }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'A New City', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('does not log when only zone/sector/location fields are updated', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "NEWZONE" sectorEN: "NEWSECTOR" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NOUVEL_ACRONYME', - city: 'Nouvelle ville', - country: 'Nouveau pays', - name: 'Nouveau nom', - province: 'Nouvelle province', - sector: 'Nouveau secteur', - zone: 'Nouvelle zone', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ logActivity }), }) + + expect(logActivity).not.toHaveBeenCalled() }) - describe('users permission level is admin', () => { - beforeEach(async () => { - await collections.affiliations.save({ - _from: org._id, - _to: user._id, - permission: 'admin', - }) - }) - describe('users language is english', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('populates initiatedBy with user key, userName, role, and IP address', async () => { + const logActivity = jest.fn() - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameEN: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Some Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ logActivity, ip: '10.0.0.1', permission: 'admin' }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneEN: "New Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(logActivity).toHaveBeenCalledWith( + expect.objectContaining({ + initiatedBy: { + id: BASE_USER._key, + userName: BASE_USER.userName, + role: 'admin', + ipAddress: '10.0.0.1', + }, + }), + ) + }) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'New Zone', - }, - }, - }, - } + describe('super_admin exclusive fields', () => { + it('super_admin can set externallyManaged to true', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorEN: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: true }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'New Sector', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryEN: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externallyManaged: true }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'A New Country', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('super_admin can set externallyManaged to false', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceEN: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: false }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'Ottawa', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'A New Province', - sector: 'TBS', - zone: 'FED', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externallyManaged: false }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TBS', - city: 'A New City', - country: 'Canada', - name: 'Treasury Board of Canada Secretariat', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('admin cannot set externallyManaged — field is omitted from update payload', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externallyManaged: true }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'en' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NEW_ACRONYM', - name: 'New Name', - zone: 'New Zone', - sector: 'New Sector', - country: 'New Country', - province: 'New Province', - city: 'New City', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ permission: 'admin', orgDS }), }) - describe('users language is french', () => { - describe('updating the acronym', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymFR: "TEST" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'TEST', - city: 'Ottawa', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externallyManaged') + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the name', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - nameFR: "Test" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + it('externallyManaged omitted from input is never set even for super_admin', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'Ottawa', - country: 'Canada', - name: 'Test', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "No Managed Field" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the zone', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - zoneFR: "Secret Zone" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externallyManaged') + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'Secret Zone', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('super_admin can update externalId', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the sector', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - sectorFR: "New Sector" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externalId: "new-ext-id" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'New Sector', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the country', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - countryFR: "A New Country" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + expect(update).toHaveBeenCalledWith( + expect.objectContaining({ + updatedOrgDetails: expect.objectContaining({ externalId: 'new-ext-id' }), + }), + ) + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'A New Country', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }, - } + it('admin cannot update externalId — field is omitted from update payload', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the province', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - provinceFR: "A New Province" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" externalId: "should-be-ignored" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'A New Province', - city: 'Ottawa', - }, - }, - }, } + } + `, + contextValue: makeContext({ permission: 'admin', orgDS }), + }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating the city', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - cityFR: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails).not.toHaveProperty('externalId') + }) - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'SCT', - city: 'A New City', - country: 'Canada', - name: 'Secrétariat du Conseil Trésor du Canada', - province: 'Ontario', - sector: 'TBS', - zone: 'FED', - }, - }, - }, - } + it('super_admin falls back to existing externalId when none is provided', async () => { + const update = jest.fn() + const orgDS = makeOrgDS({ update }) - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) - describe('updating all organizational fields', () => { - it('returns the updated organization', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', org._key)}" - acronymEN: "NEW_ACRONYM" - nameEN: "New Name" - zoneEN: "New Zone" - sectorEN: "New Sector" - countryEN: "New Country" - provinceEN: "New Province" - cityEN: "New City" - acronymFR: "NOUVEL_ACRONYME" - nameFR: "Nouveau nom" - zoneFR: "Nouvelle zone" - sectorFR: "Nouveau secteur" - countryFR: "Nouveau pays" - provinceFR: "Nouvelle province" - cityFR: "Nouvelle ville" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Some Name" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - `, - rootValue: null, - contextValue: { - query, - collections: collectionNames, - transaction, - userKey: user._key, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: checkPermission({ - userKey: user._key, - query, - }), - userRequired: userRequired({ - userKey: user._key, - loadUserByKey: loadUserByKey({ query }), - }), - verifiedRequired: verifiedRequired({}), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: loadOrgByKey({ query, language: 'fr' }), - loadUserByKey: loadUserByKey({ query }), - }, - }, - }) - - const expectedResponse = { - data: { - updateOrganization: { - result: { - acronym: 'NOUVEL_ACRONYME', - city: 'Nouvelle ville', - country: 'Nouveau pays', - name: 'Nouveau nom', - province: 'Nouvelle province', - sector: 'Nouveau secteur', - zone: 'Nouvelle zone', - }, - }, - }, } - - expect(response).toEqual(expectedResponse) - expect(consoleOutput).toEqual([`User: ${user._key}, successfully updated org ${org._key}.`]) - }) - }) + } + `, + contextValue: makeContext({ permission: 'super_admin', orgDS }), }) + + const { updatedOrgDetails } = update.mock.calls[0][0] + expect(updatedOrgDetails.externalId).toBe(BASE_RAW_ORG.externalId) }) }) - describe('given an unsuccessful organization update', () => { - let i18n - describe('users language is set to english', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'en', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('organization cannot be found', () => { - describe('organization does not exist in database', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { id: "${toGlobalId('organization', 1)}", cityEN: "A New City" } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Unable to update unknown organization.', - }, - }, - }, + describe('error: unknown organization', () => { + it('returns code 400 with correct message (EN) and logs a warning', async () => { + const orgDS = makeOrgDS({ firstLoad: undefined }) + + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization: 1, however no organizations is associated with that id.`, - ]) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 400, + description: 'Unable to update unknown organization.', }) - describe('user is located in the database', () => { - describe('user does not have the proper permissions', () => { - describe('user has user level permission', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(consoleOutput).toContain( + `User: user123 attempted to update organization: ${ORG_KEY}, however no organizations is associated with that id.`, + ) + }) + + it('returns a translated 400 error (FR)', async () => { + const orgDS = makeOrgDS({ firstLoad: undefined }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - 'Permission Denied: Please contact organization admin for help with updating organization.', - }, - }, - }, + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } } + } + } + `, + contextValue: makeContext({ i18n: frI18n, orgDS }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: user`, - ]) - }) - }) - describe('user does not belong to that organization', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.data.updateOrganization.result.code).toBe(400) + expect(response.data.updateOrganization.result.description).not.toBe('Unable to update unknown organization.') + }) + }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - 'Permission Denied: Please contact organization admin for help with updating organization.', - }, - }, - }, + describe('error: insufficient permission', () => { + it('returns code 403 with correct message for user role (EN) and logs an error', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } } + } + } + `, + contextValue: makeContext({ permission: 'user' }), + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: undefined`, - ]) - }) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 403, + description: 'Permission Denied: Please contact organization admin for help with updating organization.', }) - describe('organization name is already in use', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 1 }), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(consoleOutput).toContain( + `User: user123 attempted to update organization ${ORG_KEY}, however they do not have the correct permission level. Permission: user`, + ) + }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Organization name already in use, please choose another and try again.', - }, - }, - }, + it('returns a translated 403 error for user role (FR)', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to change the name of org: 123 however it is already in use.`, - ]) - }) + `, + contextValue: makeContext({ i18n: frI18n, permission: 'user' }), }) - describe('cursor error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next() { - throw new Error('Database error occurred.') - }, - }), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError('Unable to update organization. Please try again.')] + expect(response.data.updateOrganization.result.code).toBe(403) + expect(response.data.updateOrganization.result.description).not.toBe( + 'Permission Denied: Please contact organization admin for help with updating organization.', + ) + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) + it('returns code 403 for undefined permission (no org affiliation)', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Whatever" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ permission: null }), }) - describe('database error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) - describe('when checking to see if orgName is already in use', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.data.updateOrganization.result.code).toBe(403) + }) + }) - const error = [new GraphQLError('Unable to update organization. Please try again.')] + describe('error: organization name already in use', () => { + it('returns code 400 with correct message (EN) and logs an error', async () => { + const orgDS = makeOrgDS({ nameInUseCount: 1 }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred during name check when user: 123 attempted to update org: 123, Error: Database error occurred.`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Taken Name" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ orgDS }), }) - describe('transaction error occurs', () => { - describe('when updating/inserting new org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when committing transaction', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to update organization. Please try again.')] - - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing org: 123, err: Error: trx commit error`, - ]) - }) - }) + expect(response.errors).toBeUndefined() + expect(response.data.updateOrganization.result).toEqual({ + code: 400, + description: 'Organization name already in use, please choose another and try again.', }) + expect(consoleOutput).toContain( + `User: user123 attempted to change the name of org: ${ORG_KEY} however it is already in use.`, + ) }) - describe('users language is set to french', () => { - beforeAll(() => { - i18n = setupI18n({ - locale: 'fr', - localeData: { - en: { plurals: {} }, - fr: { plurals: {} }, - }, - locales: ['en', 'fr'], - messages: { - en: englishMessages.messages, - fr: frenchMessages.messages, - }, - }) - }) - describe('organization cannot be found', () => { - describe('organization does not exist in database', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { id: "${toGlobalId('organization', 1)}", cityEN: "A New City" } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue(undefined), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: 'Impossible de mettre à jour une organisation inconnue.', - }, - }, - }, - } + it('returns a translated 400 error when name conflicts (FR)', async () => { + const orgDS = makeOrgDS({ nameInUseCount: 1 }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization: 1, however no organizations is associated with that id.`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Taken Name" }) { + result { + ... on OrganizationError { code description } + ... on Organization { id } + } + } + } + `, + contextValue: makeContext({ i18n: frI18n, orgDS }), }) - describe('user is located in the database', () => { - describe('user does not have the proper permissions', () => { - describe('user has user level permission', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('user'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.", - }, - }, - }, - } + expect(response.data.updateOrganization.result.code).toBe(400) + expect(response.data.updateOrganization.result.description).not.toBe( + 'Organization name already in use, please choose another and try again.', + ) + }) - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: user`, - ]) - }) - }) - describe('user does not belong to that organization', () => { - it('returns an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization ( - input: { - id: "${toGlobalId('organization', 123)}" - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue(undefined), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ _id: 'organizations/123' }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + it('skips the name check when neither nameEN nor nameFR is provided', async () => { + const checkNameInUse = jest.fn() + const orgDS = makeOrgDS({ checkNameInUse }) - const error = { - data: { - updateOrganization: { - result: { - code: 403, - description: - "Permission refusée : Veuillez contacter l'administrateur de l'organisation pour obtenir de l'aide sur la suppression des utilisateurs.", - }, - }, - }, + await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "NewZone" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to update organization 123, however they do not have the correct permission level. Permission: undefined`, - ]) - }) - }) - }) - }) - describe('organization name is already in use', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ count: 1 }), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = { - data: { - updateOrganization: { - result: { - code: 400, - description: "Le nom de l'organisation est déjà utilisé, veuillez en choisir un autre et réessayer.", - }, - }, - }, + } } - - expect(response).toEqual(error) - expect(consoleOutput).toEqual([ - `User: 123 attempted to change the name of org: 123 however it is already in use.`, - ]) - }) + `, + contextValue: makeContext({ orgDS }), }) - describe('cursor error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next() { - throw new Error('Database error occurred.') - }, - }), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + expect(checkNameInUse).not.toHaveBeenCalled() + }) + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Cursor error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) + describe('error: data source failures', () => { + it('propagates error thrown by organizationDS.update', async () => { + const orgDS = makeOrgDS({ + update: jest.fn().mockRejectedValue(new Error('Unable to load organization. Please try again.')), }) - describe('database error occurs', () => { - describe('when gathering comparison org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred while retrieving org: 123 for update, err: Error: Database error occurred.`, - ]) - }) - }) - describe('when checking to see if orgName is already in use', () => { - it('throws an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - nameEN: "Treasury Board of Canada Secretariat" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockRejectedValue(new Error('Database error occurred.')), - collections: collectionNames, - transaction, - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Fail Update" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), + }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to load organization. Please try again.') + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Database error occurred during name check when user: 123 attempted to update org: 123, Error: Database error occurred.`, - ]) - }) - }) + it('propagates error thrown by organizationDS.checkNameInUse', async () => { + const orgDS = makeOrgDS({ + checkNameInUse: jest.fn().mockRejectedValue(new Error('Unable to update organization. Please try again.')), }) - describe('transaction error occurs', () => { - describe('when updating/inserting new org details', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" nameEN: "Fail Check" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when committing transaction', () => { - it('returns an error', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - updateOrganization( - input: { - id: "${toGlobalId('organization', 123)}", - cityEN: "A New City" - } - ) { - result { - ... on Organization { - acronym - name - zone - sector - country - province - city - } - ... on OrganizationError { - code - description - } - } - } - } - `, - rootValue: null, - contextValue: { - i18n, - query: jest.fn().mockReturnValue({ - next: jest.fn().mockReturnValue({ - orgDetails: { - en: { - slug: 'treasury-board-secretariat', - acronym: 'TBS', - name: 'Treasury Board of Canada Secretariat', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - fr: { - slug: 'secretariat-conseil-tresor', - acronym: 'SCT', - name: 'Secrétariat du Conseil Trésor du Canada', - zone: 'FED', - sector: 'TBS', - country: 'Canada', - province: 'Ontario', - city: 'Ottawa', - }, - }, - }), - }), - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - request: { ip: '127.0.0.1' }, - auth: { - checkPermission: jest.fn().mockReturnValue('admin'), - userRequired: jest.fn().mockReturnValue({ _key: 123 }), - verifiedRequired: jest.fn(), - }, - validators: { - cleanseInput, - slugify, - }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - name: 'Treasury Board of Canada Secretariat', - _key: 123, - }), - }, - loadUserByKey: { - load: jest.fn(), - }, - }, - }, - }) + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to update organization. Please try again.') + }) - const error = [new GraphQLError("Impossible de mettre à jour l'organisation. Veuillez réessayer.")] + it('propagates error thrown by organizationDS.getRawByKey', async () => { + const orgDS = makeOrgDS({ + getRawByKey: jest.fn().mockRejectedValue(new Error('Unable to load organization. Please try again.')), + }) - expect(response.errors).toEqual(error) - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing org: 123, err: Error: trx commit error`, - ]) - }) - }) + const response = await graphql({ + schema, + source: ` + mutation { + updateOrganization(input: { id: "${ORG_GID}" zoneEN: "FED" }) { + result { + ... on Organization { id } + ... on OrganizationError { code description } + } + } + } + `, + contextValue: makeContext({ orgDS }), }) + + expect(response.errors).toBeDefined() + expect(response.errors[0].message).toBe('Unable to load organization. Please try again.') }) }) }) diff --git a/api/src/organization/mutations/__tests__/verify-organization.test.js b/api/src/organization/mutations/__tests__/verify-organization.test.js index b70f6f3cc0..86a37edfa5 100644 --- a/api/src/organization/mutations/__tests__/verify-organization.test.js +++ b/api/src/organization/mutations/__tests__/verify-organization.test.js @@ -11,8 +11,8 @@ import frenchMessages from '../../../locale/fr/messages' import { cleanseInput } from '../../../validators' import { checkPermission, userRequired, verifiedRequired } from '../../../auth' import { loadUserByKey } from '../../../user/loaders' -import { loadOrgByKey } from '../../loaders' import { loadDomainByKey } from '../../../domain/loaders' +import { OrganizationDataSource } from '../../data-source' import dbschema from '../../../../database.json' import { collectionNames } from '../../../collection-names' @@ -172,13 +172,18 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: loadOrgByKey({ + dataSources: { + organization: new OrganizationDataSource({ query, - language: 'en', userKey: user._key, i18n, + language: 'en', + cleanseInput, + transaction, + collections: collectionNames, }), + }, + loaders: { loadUserByKey: loadUserByKey({ query, userKey: user._key, @@ -204,13 +209,8 @@ describe('removing an organization', () => { expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([`User: ${user._key}, successfully verified org: ${org._key}.`]) - const orgLoader = loadOrgByKey({ - query, - language: 'en', - userKey: user._key, - i18n, - }) - const verifiedOrg = await orgLoader.load(org._key) + const orgDS = new OrganizationDataSource({ query, userKey: user._key, i18n, language: 'en', cleanseInput, transaction, collections: collectionNames }) + const verifiedOrg = await orgDS.byKey.load(org._key) expect(verifiedOrg.verified).toEqual(true) const domainLoader = loadDomainByKey({ query, userKey: user._key, i18n }) @@ -286,13 +286,18 @@ describe('removing an organization', () => { verifiedRequired: verifiedRequired({}), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: loadOrgByKey({ + dataSources: { + organization: new OrganizationDataSource({ query, - language: 'fr', userKey: user._key, i18n, + language: 'fr', + cleanseInput, + transaction, + collections: collectionNames, }), + }, + loaders: { loadUserByKey: loadUserByKey({ query, userKey: user._key, @@ -318,13 +323,8 @@ describe('removing an organization', () => { expect(response).toEqual(expectedResponse) expect(consoleOutput).toEqual([`User: ${user._key}, successfully verified org: ${org._key}.`]) - const orgLoader = loadOrgByKey({ - query, - language: 'fr', - userKey: user._key, - i18n, - }) - const verifiedOrg = await orgLoader.load(org._key) + const orgDS = new OrganizationDataSource({ query, userKey: user._key, i18n, language: 'fr', cleanseInput, transaction, collections: collectionNames }) + const verifiedOrg = await orgDS.byKey.load(org._key) expect(verifiedOrg.verified).toEqual(true) }) }) @@ -385,11 +385,9 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -450,13 +448,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -517,13 +513,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -585,13 +579,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ verified: true, }), - }, - }, + } } }, }, }) @@ -612,192 +604,55 @@ describe('removing an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when stepping transaction', () => { - describe('when upserting org information', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } + describe('data source error occurs', () => { + it('throws an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + verifyOrganization( + input: { + orgId: "${toGlobalId('organization', 123)}" } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when clearing owners', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ) { + result { + ... on OrganizationResult { + status + organization { + name } } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - }) - describe('when committing transaction', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, + } + `, + rootValue: null, + contextValue: { + i18n, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('super_admin'), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + }, + validators: { cleanseInput }, + dataSources: { + organization: { + byKey: { load: jest.fn().mockReturnValue({ verified: false, _key: 123 }) }, + verify: jest.fn().mockRejectedValue(new Error('Unable to verify organization. Please try again.')), }, }, - }) - - const error = [new GraphQLError('Unable to verify organization. Please try again.')] + }, + }) - expect(response.errors).toEqual(error) + const error = [new GraphQLError('Unable to verify organization. Please try again.')] - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing newly verified org: 123, err: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) @@ -855,11 +710,9 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue(undefined), - }, - }, + } } }, }, }) @@ -920,13 +773,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -987,13 +838,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ _id: 'organizations/123', }), - }, - }, + } } }, }, }) @@ -1055,13 +904,11 @@ describe('removing an organization', () => { verifiedRequired: jest.fn(), }, validators: { cleanseInput }, - loaders: { - loadOrgByKey: { + dataSources: { organization: { byKey: { load: jest.fn().mockReturnValue({ verified: true, }), - }, - }, + } } }, }, }) @@ -1082,192 +929,55 @@ describe('removing an organization', () => { ]) }) }) - describe('transaction error occurs', () => { - describe('when stepping transaction', () => { - describe('when upserting org information', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } - } - } + describe('data source error occurs', () => { + it('throws an error message', async () => { + const response = await graphql({ + schema, + source: ` + mutation { + verifyOrganization( + input: { + orgId: "${toGlobalId('organization', 123)}" } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - describe('when clearing owners', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ) { + result { + ... on OrganizationResult { + status + organization { + name } } - } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockRejectedValue(new Error('trx step error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, - }, - }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - - expect(response.errors).toEqual(error) - - expect(consoleOutput).toEqual([ - `Transaction error occurred while upserting verified org: 123, err: Error: trx step error`, - ]) - }) - }) - }) - describe('when committing transaction', () => { - it('throws an error message', async () => { - const response = await graphql({ - schema, - source: ` - mutation { - verifyOrganization( - input: { - orgId: "${toGlobalId('organization', 123)}" - } - ) { - result { - ... on OrganizationResult { - status - organization { - name - } - } - ... on OrganizationError { - code - description - } + ... on OrganizationError { + code + description } } } - `, - rootValue: null, - contextValue: { - i18n, - query, - collections: collectionNames, - transaction: jest.fn().mockReturnValue({ - step: jest.fn().mockReturnValue(), - commit: jest.fn().mockRejectedValue(new Error('trx commit error')), - abort: jest.fn(), - }), - userKey: 123, - auth: { - checkPermission: jest.fn().mockReturnValue('super_admin'), - userRequired: jest.fn(), - verifiedRequired: jest.fn(), - }, - validators: { cleanseInput }, - loaders: { - loadOrgByKey: { - load: jest.fn().mockReturnValue({ - verified: false, - }), - }, + } + `, + rootValue: null, + contextValue: { + i18n, + userKey: 123, + auth: { + checkPermission: jest.fn().mockReturnValue('super_admin'), + userRequired: jest.fn(), + verifiedRequired: jest.fn(), + }, + validators: { cleanseInput }, + dataSources: { + organization: { + byKey: { load: jest.fn().mockReturnValue({ verified: false, _key: 123 }) }, + verify: jest.fn().mockRejectedValue(new Error("Impossible de vérifier l'organisation. Veuillez réessayer.")), }, }, - }) - - const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] + }, + }) - expect(response.errors).toEqual(error) + const error = [new GraphQLError("Impossible de vérifier l'organisation. Veuillez réessayer.")] - expect(consoleOutput).toEqual([ - `Transaction error occurred while committing newly verified org: 123, err: Error: trx commit error`, - ]) - }) + expect(response.errors).toEqual(error) + expect(consoleOutput).toEqual([]) }) }) }) diff --git a/api/src/organization/mutations/archive-organization.js b/api/src/organization/mutations/archive-organization.js index a2fd937fd6..583fa0de54 100644 --- a/api/src/organization/mutations/archive-organization.js +++ b/api/src/organization/mutations/archive-organization.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import ac from '../../access-control' export const archiveOrganization = new mutationWithClientMutationId({ @@ -26,14 +25,11 @@ export const archiveOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, validators: { cleanseInput }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, }, ) => { // Get user @@ -45,7 +41,7 @@ export const archiveOrganization = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get org from db - const organization = await loadOrgByKey.load(orgId) + const organization = await organizationDS.byKey.load(orgId) // Check to see if org exists if (!organization) { @@ -71,103 +67,11 @@ export const archiveOrganization = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - - // check to see if any other orgs are using this domain - let countCursor - try { - countCursor = await query` - WITH claims, domains, organizations - LET domainIds = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - RETURN e._to - ) - FOR domain IN domains - FILTER domain._id IN domainIds - LET count = LENGTH( - FOR v, e IN 1..1 INBOUND domain._id claims - RETURN 1 - ) - RETURN { - _id: domain._id, - _key: domain._key, - domain: domain.domain, - count - } - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to gather domain count while archiving org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - let domainInfo - try { - domainInfo = await countCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to gather domain count while archiving org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - for (const domain of domainInfo) { - if (domain.count === 1) { - try { - // Archive domain - await trx.step( - async () => - await query` - WITH domains - UPDATE { _key: ${domain._key}, archived: true } IN domains - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to archive domains while archiving org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - } - } - - try { - await trx.step( - () => - query` - WITH organizations - UPDATE { _key: ${organization._key}, verified: false } IN organizations - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to unverify while archiving org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred for user: ${userKey} while attempting archive of org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to archive organization. Please try again.`)) - } + await organizationDS.archive({ organization }) console.info(`User: ${userKey} successfully archived org: ${organization._key}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -186,8 +90,8 @@ export const archiveOrganization = new mutationWithClientMutationId({ resource: { en: organization.name, fr: organization.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/create-organization.js b/api/src/organization/mutations/create-organization.js index 3ff0b0364f..10c2d638e4 100644 --- a/api/src/organization/mutations/create-organization.js +++ b/api/src/organization/mutations/create-organization.js @@ -4,7 +4,6 @@ import { t } from '@lingui/macro' import { Acronym } from '../../scalars' import { createOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' export const createOrganization = new mutationWithClientMutationId({ name: 'CreateOrganization', @@ -47,13 +46,10 @@ export const createOrganization = new mutationWithClientMutationId({ { i18n, request, - collections, - transaction, - query, userKey, request: { ip }, auth: { userRequired, verifiedRequired, checkSuperAdmin, superAdminRequired }, - loaders: { loadOrgBySlug }, + dataSources: { auditLogs, organization: organizationDS }, validators: { cleanseInput, slugify }, }, ) => { @@ -79,7 +75,7 @@ export const createOrganization = new mutationWithClientMutationId({ const slugFR = slugify(nameFR) // Check to see if org already exists - const [orgEN, orgFR] = await loadOrgBySlug.loadMany([slugEN, slugFR]) + const [orgEN, orgFR] = await organizationDS.bySlug.loadMany([slugEN, slugFR]) if (typeof orgEN !== 'undefined' || typeof orgFR !== 'undefined') { console.warn(`User: ${userKey} attempted to create an organization that already exists: ${slugEN}`) @@ -90,7 +86,6 @@ export const createOrganization = new mutationWithClientMutationId({ } } - // Create new organization const organizationDetails = { verified: args.verified || false, externallyManaged: false, @@ -109,73 +104,14 @@ export const createOrganization = new mutationWithClientMutationId({ }, } - // Setup Trans action - const trx = await transaction(collections) - - let cursor - try { - cursor = await trx.step( - () => - query` - WITH organizations - INSERT ${organizationDetails} INTO organizations - RETURN MERGE( - { - _id: NEW._id, - _key: NEW._key, - _rev: NEW._rev, - _type: "organization", - id: NEW._key, - verified: NEW.verified, - domainCount: 0, - summaries: NEW.summaries - }, - TRANSLATE(${request.language}, NEW.orgDetails) - ) - `, - ) - } catch (err) { - console.error(`Transaction error occurred when user: ${userKey} was creating new organization ${slugEN}: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } - const organization = await cursor.next() - - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - INSERT { - _from: ${organization._id}, - _to: ${user._id}, - permission: "owner", - } INTO affiliations - `, - ) - } catch (err) { - console.error( - `Transaction error occurred when inserting edge definition for user: ${userKey} to ${slugEN}: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Transaction error occurred when committing new organization: ${slugEN} for user: ${userKey} to db: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to create organization. Please try again.`)) - } + const organization = await organizationDS.create({ + organizationDetails, + userId: user._id, + language: request.language, + }) console.info(`User: ${userKey} successfully created a new organization: ${slugEN}`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -186,8 +122,8 @@ export const createOrganization = new mutationWithClientMutationId({ resource: { en: organizationDetails.orgDetails.en.name, fr: organizationDetails.orgDetails.fr.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/remove-organization.js b/api/src/organization/mutations/remove-organization.js index c9c848ff48..114566b58b 100644 --- a/api/src/organization/mutations/remove-organization.js +++ b/api/src/organization/mutations/remove-organization.js @@ -3,7 +3,6 @@ import { mutationWithClientMutationId, fromGlobalId } from 'graphql-relay' import { t } from '@lingui/macro' import { removeOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import ac from '../../access-control' export const removeOrganization = new mutationWithClientMutationId({ @@ -26,14 +25,11 @@ export const removeOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, validators: { cleanseInput }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, }, ) => { // Get user @@ -45,7 +41,7 @@ export const removeOrganization = new mutationWithClientMutationId({ const { type: _orgType, id: orgId } = fromGlobalId(cleanseInput(args.orgId)) // Get org from db - const organization = await loadOrgByKey.load(orgId) + const organization = await organizationDS.byKey.load(orgId) // Check to see if org exists if (!organization) { @@ -85,319 +81,11 @@ export const removeOrganization = new mutationWithClientMutationId({ } } - // Setup Trans action - const trx = await transaction(collections) - - // check to see if org has any dmarc summaries - let dmarcSummaryCheckCursor - try { - dmarcSummaryCheckCursor = await query` - WITH domains, ownership, dmarcSummaries, organizations - FOR v, e IN 1..1 OUTBOUND ${organization._id} ownership - RETURN e - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to get dmarcSummaryInfo while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - let dmarcSummaryCheckList - try { - dmarcSummaryCheckList = await dmarcSummaryCheckCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to get dmarcSummaryInfo while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - for (const ownership of dmarcSummaryCheckList) { - try { - await trx.step( - () => query` - WITH ownership, organizations, domains, dmarcSummaries, domainsToDmarcSummaries - LET dmarcSummaryEdges = ( - FOR v, e IN 1..1 OUTBOUND ${ownership._to} domainsToDmarcSummaries - RETURN { edgeKey: e._key, dmarcSummaryId: e._to } - ) - LET removeDmarcSummaryEdges = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - REMOVE dmarcSummaryEdge.edgeKey IN domainsToDmarcSummaries - OPTIONS { waitForSync: true } - ) - LET removeDmarcSummary = ( - FOR dmarcSummaryEdge IN dmarcSummaryEdges - LET key = PARSE_IDENTIFIER(dmarcSummaryEdge.dmarcSummaryId).key - REMOVE key IN dmarcSummaries - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove dmarc summaries while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - await trx.step( - () => query` - WITH ownership, organizations, domains - REMOVE ${ownership._key} IN ownership - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove ownerships while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - } - - // check to see if any other orgs are using this domain - let countCursor - try { - countCursor = await query` - WITH claims, domains, organizations - LET domainIds = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - RETURN e._to - ) - FOR domain IN domains - FILTER domain._id IN domainIds - LET count = LENGTH( - FOR v, e IN 1..1 INBOUND domain._id claims - RETURN 1 - ) - RETURN { - "_id": domain._id, - "_key": domain._key, - "domain": domain.domain, - "count": count - } - ` - } catch (err) { - console.error( - `Database error occurred for user: ${userKey} while attempting to gather domain count while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - let domainInfo - try { - domainInfo = await countCursor.all() - } catch (err) { - console.error( - `Cursor error occurred for user: ${userKey} while attempting to gather domain count while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - for (const domain of domainInfo) { - if (domain.count === 1) { - try { - // Remove web data - await trx.step(async () => { - await query` - WITH web, webScan - FOR webV, domainsWebEdge IN 1..1 OUTBOUND ${domain._id} domainsWeb - LET removeWebScansQuery = ( - FOR webScanV, webToWebScansV In 1..1 OUTBOUND webV._id webToWebScans - REMOVE webScanV IN webScan - REMOVE webToWebScansV IN webToWebScans - OPTIONS { waitForSync: true } - ) - REMOVE webV IN web - REMOVE domainsWebEdge IN domainsWeb - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove web data for ${domain.domain} in org: ${organization.slug}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - // Remove DNS data - await trx.step(async () => { - await query` - WITH dns - FOR dnsV, domainsDNSEdge IN 1..1 OUTBOUND ${domain._id} domainsDNS - REMOVE dnsV IN dns - REMOVE domainsDNSEdge IN domainsDNS - OPTIONS { waitForSync: true } - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DNS data for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - // remove favourites - try { - await trx.step(async () => { - await query` - WITH favourites, domains - FOR fav IN favourites - FILTER fav._to == ${domain._id} - REMOVE fav IN favourites - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove favourites for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - // remove DKIM selectors - try { - await trx.step(async () => { - await query` - FOR e IN domainsToSelectors - FILTER e._from == ${domain._id} - REMOVE e IN domainsToSelectors - ` - }) - } catch (err) { - console.error( - `Trx step error occurred while user: ${userKey} attempted to remove DKIM selectors for ${domain.domain} in org: ${organization.slug}, error: ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - // Remove domain - await trx.step( - () => - query` - WITH claims, domains, organizations - LET domainEdges = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} claims - FILTER e._to == ${domain._id} - RETURN { edgeKey: e._key, domainId: e._to } - ) - LET removeDomainEdges = ( - FOR domainEdge in domainEdges - REMOVE domainEdge.edgeKey IN claims - OPTIONS { waitForSync: true } - ) - LET removeDomain = ( - FOR domainEdge in domainEdges - LET key = PARSE_IDENTIFIER(domainEdge.domainId).key - REMOVE key IN domains - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove domains while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - } - } - - let orgCursor - let compareOrg - if (typeof organization !== 'undefined') { - // Get all org details for comparison - try { - orgCursor = await query` - WITH organizations - FOR org IN organizations - FILTER org._key == ${organization._key} - RETURN org - ` - } catch (err) {} - - try { - compareOrg = await orgCursor.next() - } catch (err) {} - } - - try { - await trx.step( - () => - query` - WITH affiliations, organizations, users - LET userEdges = ( - FOR v, e IN 1..1 OUTBOUND ${organization._id} affiliations - RETURN { edgeKey: e._key, userKey: e._to } - ) - LET removeUserEdges = ( - FOR userEdge IN userEdges - REMOVE userEdge.edgeKey IN affiliations - OPTIONS { waitForSync: true } - ) - RETURN true - `, - ) - - await trx.step( - () => - query` - WITH organizations, organizationSummaries - FOR summary in organizationSummaries - FILTER summary.organization == ${organization._id} - REMOVE summary._key IN organizationSummaries - OPTIONS { waitForSync: true } - `, - ) - - await trx.step( - () => - query` - WITH organizations - REMOVE ${organization._key} IN organizations - OPTIONS { waitForSync: true } - `, - ) - } catch (err) { - console.error( - `Trx step error occurred for user: ${userKey} while attempting to remove affiliations, and the org while removing org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error( - `Trx commit error occurred for user: ${userKey} while attempting remove of org: ${organization._key}, ${err}`, - ) - await trx.abort() - throw new Error(i18n._(t`Unable to remove organization. Please try again.`)) - } + const compareOrg = await organizationDS.getRawByKey({ orgKey: organization._key }) + await organizationDS.remove({ organization }) console.info(`User: ${userKey} successfully removed org: ${organization._key}.`) - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -407,10 +95,10 @@ export const removeOrganization = new mutationWithClientMutationId({ action: 'delete', target: { resource: { - en: compareOrg.orgDetails.en.name || organization.name, - fr: compareOrg.orgDetails.fr.name || organization.name, - }, // name of resource being acted upon - resourceType: 'organization', // user, org, domain + en: compareOrg?.orgDetails.en.name || organization.name, + fr: compareOrg?.orgDetails.fr.name || organization.name, + }, + resourceType: 'organization', }, }) diff --git a/api/src/organization/mutations/update-organization.js b/api/src/organization/mutations/update-organization.js index 60416784c2..8e6938a2e7 100644 --- a/api/src/organization/mutations/update-organization.js +++ b/api/src/organization/mutations/update-organization.js @@ -4,7 +4,6 @@ import { t } from '@lingui/macro' import { Acronym } from '../../scalars' import { updateOrganizationUnion } from '../unions' -import { logActivity } from '../../audit-logs/mutations/log-activity' import ac from '../../access-control' export const updateOrganization = new mutationWithClientMutationId({ @@ -91,13 +90,10 @@ export const updateOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, request: { ip }, auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadOrgByKey }, + dataSources: { auditLogs, organization: organizationDS }, validators: { cleanseInput, slugify }, }, ) => { @@ -129,7 +125,7 @@ export const updateOrganization = new mutationWithClientMutationId({ const slugFR = slugify(nameFR) // Check to see if org exists - const currentOrg = await loadOrgByKey.load(orgKey) + const currentOrg = await organizationDS.byKey.load(orgKey) if (typeof currentOrg === 'undefined') { console.warn( @@ -160,21 +156,7 @@ export const updateOrganization = new mutationWithClientMutationId({ // Check to see if any orgs already have the name in use if (nameEN !== '' || nameFR !== '') { - let orgNameCheckCursor - try { - orgNameCheckCursor = await query` - WITH organizations - FOR org IN organizations - FILTER (org.orgDetails.en.name == ${nameEN}) OR (org.orgDetails.fr.name == ${nameFR}) - RETURN org - ` - } catch (err) { - console.error( - `Database error occurred during name check when user: ${userKey} attempted to update org: ${currentOrg._key}, ${err}`, - ) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } - + const orgNameCheckCursor = await organizationDS.checkNameInUse({ nameEN, nameFR }) if (orgNameCheckCursor.count > 0) { console.error( `User: ${userKey} attempted to change the name of org: ${currentOrg._key} however it is already in use.`, @@ -187,27 +169,7 @@ export const updateOrganization = new mutationWithClientMutationId({ } } - // Get all org details for comparison - let orgCursor - try { - orgCursor = await query` - WITH organizations - FOR org IN organizations - FILTER org._key == ${orgKey} - RETURN org - ` - } catch (err) { - console.error(`Database error occurred while retrieving org: ${orgKey} for update, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } - - let compareOrg - try { - compareOrg = await orgCursor.next() - } catch (err) { - console.error(`Cursor error occurred while retrieving org: ${orgKey} for update, err: ${err}`) - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } + const compareOrg = await organizationDS.getRawByKey({ orgKey }) const updatedOrgDetails = { orgDetails: { @@ -242,37 +204,10 @@ export const updateOrganization = new mutationWithClientMutationId({ updatedOrgDetails.externalId = externalId || compareOrg?.externalId } - // Setup Trans action - const trx = await transaction(collections) - - // Upsert new org details - try { - await trx.step( - async () => - await query` - WITH organizations - UPSERT { _key: ${orgKey} } - INSERT ${updatedOrgDetails} - UPDATE ${updatedOrgDetails} - IN organizations - `, - ) - } catch (err) { - console.error(`Transaction error occurred while upserting org: ${orgKey}, err: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction error occurred while committing org: ${orgKey}, err: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to update organization. Please try again.`)) - } + await organizationDS.update({ orgKey, updatedOrgDetails }) - await loadOrgByKey.clear(orgKey) - const organization = await loadOrgByKey.load(orgKey) + await organizationDS.byKey.clear(orgKey) + const organization = await organizationDS.byKey.load(orgKey) console.info(`User: ${userKey}, successfully updated org ${orgKey}.`) @@ -306,10 +241,7 @@ export const updateOrganization = new mutationWithClientMutationId({ }) } if (updatedProperties.length > 0) { - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -322,7 +254,7 @@ export const updateOrganization = new mutationWithClientMutationId({ en: compareOrg.orgDetails.en.name, fr: compareOrg.orgDetails.fr.name, }, - resourceType: 'organization', // user, org, domain + resourceType: 'organization', updatedProperties, }, }) diff --git a/api/src/organization/mutations/verify-organization.js b/api/src/organization/mutations/verify-organization.js index a09f64d886..dbb2bb2530 100644 --- a/api/src/organization/mutations/verify-organization.js +++ b/api/src/organization/mutations/verify-organization.js @@ -24,12 +24,9 @@ export const verifyOrganization = new mutationWithClientMutationId({ args, { i18n, - query, - collections, - transaction, userKey, auth: { checkPermission, userRequired, verifiedRequired }, - loaders: { loadOrgByKey }, + dataSources: { organization: organizationDS }, validators: { cleanseInput }, }, ) => { @@ -41,7 +38,7 @@ export const verifyOrganization = new mutationWithClientMutationId({ const { id: orgKey } = fromGlobalId(cleanseInput(args.orgId)) // Check to see if org exists - const currentOrg = await loadOrgByKey.load(orgKey) + const currentOrg = await organizationDS.byKey.load(orgKey) if (typeof currentOrg === 'undefined') { console.warn( @@ -82,54 +79,9 @@ export const verifyOrganization = new mutationWithClientMutationId({ } } - // Set org to verified currentOrg.verified = true - // Setup Trans action - const trx = await transaction(collections) - - // Upsert new org details - try { - await trx.step( - () => - query` - WITH organizations - UPSERT { _key: ${orgKey} } - INSERT ${currentOrg} - UPDATE ${currentOrg} - IN organizations - `, - ) - } catch (err) { - console.error(`Transaction error occurred while upserting verified org: ${orgKey}, err: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } - - // unarchive all archived affiliated domains - try { - await trx.step( - () => - query` - WITH domains, claims - FOR v, e IN 1..1 OUTBOUND ${currentOrg._id} claims - FILTER v.archived == true - UPDATE v WITH { archived: false } IN domains - `, - ) - } catch (err) { - console.error(`Transaction error occurred while unarchiving affiliated domains for org: ${orgKey}, err: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } - - try { - await trx.commit() - } catch (err) { - console.error(`Transaction error occurred while committing newly verified org: ${orgKey}, err: ${err}`) - await trx.abort() - throw new Error(i18n._(t`Unable to verify organization. Please try again.`)) - } + await organizationDS.verify({ currentOrg }) console.info(`User: ${userKey}, successfully verified org: ${orgKey}.`) diff --git a/api/src/organization/objects/organization.js b/api/src/organization/objects/organization.js index 78e3c5ac03..58e7b0e722 100644 --- a/api/src/organization/objects/organization.js +++ b/api/src/organization/objects/organization.js @@ -9,7 +9,6 @@ import { affiliationUserOrder } from '../../affiliation/inputs' import { affiliationConnection } from '../../affiliation/objects' import { domainOrder, domainFilter } from '../../domain/inputs' import { domainConnection } from '../../domain/objects' -import { logActivity } from '../../audit-logs' import { OrderDirection } from '../../enums' import { tagType } from '../../tags/objects' import ac from '../../access-control' @@ -93,14 +92,14 @@ export const organizationType = new GraphQLObjectType({ resolve: async ( { _key }, args, - { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, loaders: { loadTagsByOrg } }, + { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, dataSources: { tags } }, ) => { if (loginRequiredBool) { const user = await userRequired() verifiedRequired({ user }) } - const orgTags = await loadTagsByOrg({ + const orgTags = await tags.byOrg({ orgId: _key, ...args, }) @@ -142,7 +141,7 @@ export const organizationType = new GraphQLObjectType({ { userKey, auth: { userRequired, loginRequiredBool, verifiedRequired }, - loaders: { loadOrganizationSummariesByPeriod }, + dataSources: { organization: organizationDS }, }, ) => { if (loginRequiredBool) { @@ -150,7 +149,7 @@ export const organizationType = new GraphQLObjectType({ verifiedRequired({ user }) } - const historicalSummaries = await loadOrganizationSummariesByPeriod({ + const historicalSummaries = await organizationDS.summariesByPeriod({ orgId: _id, ...args, }) @@ -180,13 +179,9 @@ export const organizationType = new GraphQLObjectType({ { i18n, userKey, - query, - transaction, - collections, request: { ip }, auth: { userRequired, verifiedRequired }, - dataSources: { auth: authDS }, - loaders: { loadOrganizationDomainStatuses, loadOrganizationNamesById }, + dataSources: { auth: authDS, auditLogs, organization: organizationDS }, }, ) => { const user = await userRequired() @@ -200,7 +195,7 @@ export const organizationType = new GraphQLObjectType({ throw new Error(t`Permission Denied: Please contact organization user for help with retrieving this domain.`) } - const domains = await loadOrganizationDomainStatuses({ + const domains = await organizationDS.domainStatuses({ orgId: _id, ...args, }) @@ -262,7 +257,7 @@ export const organizationType = new GraphQLObjectType({ // Get org names to use in activity log let orgNames try { - orgNames = await loadOrganizationNamesById.load(_id) + orgNames = await organizationDS.namesById.load(_id) } catch (err) { console.error( `Error occurred when user: ${userKey} attempted to export org: ${_id}. Error while retrieving organization names. error: ${err}`, @@ -270,10 +265,7 @@ export const organizationType = new GraphQLObjectType({ throw new Error(i18n._(t`Unable to export organization. Please try again.`)) } - await logActivity({ - transaction, - collections, - query, + await auditLogs.logActivity({ initiatedBy: { id: user._key, userName: user.userName, @@ -289,7 +281,7 @@ export const organizationType = new GraphQLObjectType({ organization: { id: _id, name: orgNames.orgNameEN, - }, // name of resource being acted upon + }, resourceType: 'organization', }, }) @@ -323,10 +315,10 @@ export const organizationType = new GraphQLObjectType({ { _id }, args, - { dataSources: { auth: authDS }, loaders: { loadDomainConnectionsByOrgId } }, + { dataSources: { auth: authDS, domain: domainDataSource } }, ) => { const permission = await authDS.permissionByOrgId.load(_id) - const connections = await loadDomainConnectionsByOrgId({ + const connections = await domainDataSource.connectionsByOrgId({ orgId: _id, permission, ...args, diff --git a/api/src/organization/queries/__tests__/find-my-organizations.test.js b/api/src/organization/queries/__tests__/find-my-organizations.test.js index 1440fb5e41..11ab3ea768 100644 --- a/api/src/organization/queries/__tests__/find-my-organizations.test.js +++ b/api/src/organization/queries/__tests__/find-my-organizations.test.js @@ -194,15 +194,13 @@ describe('given findMyOrganizationsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', - }), - }, + }) } }, }, }) @@ -337,15 +335,13 @@ describe('given findMyOrganizationsQuery', () => { }), verifiedRequired: verifiedRequired({}), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'fr', - }), - }, + }) } }, }, }) @@ -457,16 +453,14 @@ describe('given findMyOrganizationsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query: mockedQuery, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', i18n, - }), - }, + }) } }, }, }) @@ -535,16 +529,14 @@ describe('given findMyOrganizationsQuery', () => { userRequired: jest.fn().mockReturnValue({}), verifiedRequired: jest.fn(), }, - loaders: { - loadOrgConnectionsByUserId: loadOrgConnectionsByUserId({ + dataSources: { organization: { connectionsByUserId: loadOrgConnectionsByUserId({ query: mockedQuery, userKey: user._key, cleanseInput, auth: { loginRequired: true }, language: 'en', i18n, - }), - }, + }) } }, }, }) diff --git a/api/src/organization/queries/__tests__/find-organization-by-slug.test.js b/api/src/organization/queries/__tests__/find-organization-by-slug.test.js index 8a99ceb76c..cfb39fda7a 100644 --- a/api/src/organization/queries/__tests__/find-organization-by-slug.test.js +++ b/api/src/organization/queries/__tests__/find-organization-by-slug.test.js @@ -159,9 +159,13 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, + dataSources: { + organization: { + byKey: loadOrgByKey(query, 'en'), + bySlug: loadOrgBySlug({ query, language: 'en' }), + }, + }, loaders: { - loadOrgByKey: loadOrgByKey(query, 'en'), - loadOrgBySlug: loadOrgBySlug({ query, language: 'en' }), loadUserByKey: loadUserByKey({ query }), loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ query, @@ -265,9 +269,13 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, + dataSources: { + organization: { + byKey: loadOrgByKey(query, 'fr'), + bySlug: loadOrgBySlug({ query, language: 'fr' }), + }, + }, loaders: { - loadOrgByKey: loadOrgByKey(query, 'fr'), - loadOrgBySlug: loadOrgBySlug({ query, language: 'fr' }), loadUserByKey: loadUserByKey({ query }), loadDomainConnectionsByOrgId: loadDomainConnectionsByOrgId({ query, @@ -361,11 +369,9 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue(), - }, - }, + } } }, }, }) @@ -410,11 +416,9 @@ describe('given findOrganizationBySlugQuery', () => { validators: { cleanseInput, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue({}), - }, - }, + } } }, }, }) @@ -477,11 +481,9 @@ describe('given findOrganizationBySlugQuery', () => { cleanseInput, auth: { loginRequiredBool: true }, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue(), - }, - }, + } } }, }, }) @@ -526,11 +528,9 @@ describe('given findOrganizationBySlugQuery', () => { validators: { cleanseInput, }, - loaders: { - loadOrgBySlug: { + dataSources: { organization: { bySlug: { load: jest.fn().mockReturnValue({}), - }, - }, + } } }, }, }) diff --git a/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js b/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js index 059e224b09..b8a25f8328 100644 --- a/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js +++ b/api/src/organization/queries/__tests__/get-all-organization-domain-statuses.test.js @@ -200,13 +200,11 @@ describe('given getAllOrganizationDomainStatuses', () => { superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + }) } }, }, }) const error = [ @@ -258,14 +256,12 @@ describe('given getAllOrganizationDomainStatuses', () => { superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, language: 'en', - }), - }, + }) } }, }, }) @@ -318,13 +314,11 @@ describe('given getAllOrganizationDomainStatuses', () => { superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, - }), - }, + }) } }, }, }) const error = [ @@ -377,14 +371,12 @@ describe('given getAllOrganizationDomainStatuses', () => { superAdminRequired: superAdminRequired({ i18n }), loginRequiredBool: loginRequiredBool, }, - loaders: { - loadAllOrganizationDomainStatuses: loadAllOrganizationDomainStatuses({ + dataSources: { organization: { allDomainStatuses: loadAllOrganizationDomainStatuses({ query, userKey: user._key, i18n, language: 'en', - }), - }, + }) } }, }, }) const expectedResponse = { diff --git a/api/src/organization/queries/find-my-organizations.js b/api/src/organization/queries/find-my-organizations.js index cc622c35ff..507de5032f 100644 --- a/api/src/organization/queries/find-my-organizations.js +++ b/api/src/organization/queries/find-my-organizations.js @@ -40,7 +40,7 @@ export const findMyOrganizations = { { userKey, auth: { checkSuperAdmin, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadOrgConnectionsByUserId }, + dataSources: { organization: organizationDS }, }, ) => { if (loginRequiredBool) { @@ -50,7 +50,7 @@ export const findMyOrganizations = { const isSuperAdmin = await checkSuperAdmin() - const orgConnections = await loadOrgConnectionsByUserId({ + const orgConnections = await organizationDS.connectionsByUserId({ isSuperAdmin, ...args, }) diff --git a/api/src/organization/queries/find-organization-by-slug.js b/api/src/organization/queries/find-organization-by-slug.js index fead4c6381..90919afe5a 100644 --- a/api/src/organization/queries/find-organization-by-slug.js +++ b/api/src/organization/queries/find-organization-by-slug.js @@ -21,7 +21,7 @@ export const findOrganizationBySlug = { i18n, userKey, auth: { checkPermission, userRequired, verifiedRequired, loginRequiredBool }, - loaders: { loadOrgBySlug }, + dataSources: { organization: organizationDS }, validators: { cleanseInput }, }, ) => { @@ -35,7 +35,7 @@ export const findOrganizationBySlug = { const orgSlug = cleanseInput(args.orgSlug) // Retrieve organization by slug - const org = await loadOrgBySlug.load(orgSlug) + const org = await organizationDS.bySlug.load(orgSlug) if (typeof org === 'undefined') { console.warn(`User ${userKey} could not retrieve organization.`) diff --git a/api/src/organization/queries/get-all-organization-domain-statuses.js b/api/src/organization/queries/get-all-organization-domain-statuses.js index faadfb512b..f982c98a2c 100644 --- a/api/src/organization/queries/get-all-organization-domain-statuses.js +++ b/api/src/organization/queries/get-all-organization-domain-statuses.js @@ -18,7 +18,7 @@ export const getAllOrganizationDomainStatuses = { { userKey, auth: { checkSuperAdmin, userRequired, verifiedRequired, superAdminRequired }, - loaders: { loadAllOrganizationDomainStatuses }, + dataSources: { organization: organizationDS }, }, ) => { const user = await userRequired() @@ -27,7 +27,7 @@ export const getAllOrganizationDomainStatuses = { const isSuperAdmin = await checkSuperAdmin() superAdminRequired({ user, isSuperAdmin }) - const domainStatuses = await loadAllOrganizationDomainStatuses({ ...args }) + const domainStatuses = await organizationDS.allDomainStatuses({ ...args }) console.info(`User ${userKey} successfully retrieved all domain statuses.`) diff --git a/api/src/user/objects/my-tracker-result.js b/api/src/user/objects/my-tracker-result.js index 517ba42d8c..4b10b511bc 100644 --- a/api/src/user/objects/my-tracker-result.js +++ b/api/src/user/objects/my-tracker-result.js @@ -36,8 +36,8 @@ export const myTrackerType = new GraphQLObjectType({ }, ...connectionArgs, }, - resolve: async ({ _id }, args, { loaders: { loadDomainConnectionsByUserId } }) => { - const connections = await loadDomainConnectionsByUserId({ + resolve: async ({ _id }, args, { dataSources: { domain: domainDataSource } }) => { + const connections = await domainDataSource.connectionsByUserId({ ...args, myTracker: true, })