Skip to content

Commit

Permalink
feat: Add sourceAccountNormalizer service
Browse files Browse the repository at this point in the history
This service will normalize the cozyMetadata.sourceAccountIdentifier
attribute in io.cozy.identities,io.cozy.bills,io.cozy.accounts doctypes.

This attribute is needed for debugging purpose for identities and bills
and having this directly specified in accounts without needing any
algorithm is easier to use.

This service must be run manually at the moment since we don't want tu
run it multiple times for every konnector which updates all these
doctypes at one and I did not find a way to specify a unique @event trigger
for multiple doctypes.
  • Loading branch information
doubleface authored and doubleface committed Mar 6, 2024
1 parent 294e8f3 commit 10e2a51
Show file tree
Hide file tree
Showing 3 changed files with 496 additions and 0 deletions.
5 changes: 5 additions & 0 deletions manifest.webapp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@
"type": "node",
"file": "services/cliskTimeout/home.js",
"comment": "This service must be called programmatically from the flagship app. It stopps the given job with a context deadline exceeded error"
},
"sourceAccountIdentifierNormalizer": {
"type": "node",
"file": "services/sourceAccountIdentifierNormalizer/home.js",
"comment": "This service must be called manually"
}
}
}
185 changes: 185 additions & 0 deletions src/targets/services/sourceAccountIdentifierNormalizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* This service updates identity, bills and accounts with proper sourceAccountIdentifier in
* cozyMetadata.sourceAccountIdentifier when possible
*
*/
import CozyClient, { Q, models } from 'cozy-client'
import logger from 'cozy-logger'
import polyfillFetch from './polyfillFetch'

const log = logger.namespace('sourceAccountIdentifierNormalizer')

polyfillFetch()

const main = async () => {
const client = CozyClient.fromEnv()

await Promise.all([
normalizeIdentities(client),
normalizeBills(client),
normalizeAccounts(client)
])
}
;(async () => {
try {
await main()
} catch (error) {
log('critical', error.message)
}
})()

async function createBillsFilesMap({ client, bills }) {
const billsInvoicesIds = bills.map(bill => {
return bill.invoice?.split(':').pop()
})
const files = await client.queryAll(
Q('io.cozy.files').getByIds(billsInvoicesIds)
)
return files.reduce((map, file) => {
map.set(file._id, file)
return map
}, new Map())
}

async function createBillsAndFilesAccountsMap({
client,
bills,
billsFilesMap
}) {
const billsAndFilesAccountIds = [
...bills.map(bill => bill?.cozyMetadata?.sourceAccount).filter(Boolean),
...Array.from(billsFilesMap.values())
.map(file => file?.cozyMetadata?.sourceAccount)
.filter(Boolean)
]
const billsAndFilesAccounts = await client.queryAll(
Q('io.cozy.accounts').getByIds(billsAndFilesAccountIds)
)
return billsAndFilesAccounts.reduce((map, account) => {
map.set(account._id, account)
return map
}, new Map())
}

export async function normalizeIdentities(client) {
const identities = await client.queryAll(
Q('io.cozy.identities').partialIndex({
'cozyMetadata.sourceAccountIdentifier': { $exists: false },
identifier: { $exists: true }
})
)

const normalizedIdentities = identities
.map(identity => ({
...identity,
cozyMetadata: {
...identity.cozyMetadata,
sourceAccountIdentifier: identity.identifier
}
}))
.filter(Boolean)

if (normalizedIdentities.length > 0) {
await client.saveAll(normalizedIdentities)
}
log('info', `Normalized ${normalizedIdentities.length}/${identities.length}`)
}

export async function normalizeBills(client) {
const bills = await client.queryAll(
Q('io.cozy.bills').partialIndex({
'cozyMetadata.sourceAccountIdentifier': { $exists: false }
})
)

const billsFilesMap = await createBillsFilesMap({ client, bills })
const billsAndFilesAccountsMap = await createBillsAndFilesAccountsMap({
client,
bills,
billsFilesMap
})

const normalizedBills = bills
.map(bill => {
let sourceAccountIdentifier = null
if (bill.sourceAccountIdentifier) {
sourceAccountIdentifier = bill.sourceAccountIdentifier
} else if (bill.cozyMetadata?.sourceAccount) {
const account = billsAndFilesAccountsMap.get(
bill.cozyMetadata.sourceAccount
)
if (account) {
if (account.cozyMetadata?.sourceAccountIdentifier) {
sourceAccountIdentifier =
account.cozyMetadata.sourceAccountIdentifier
} else {
const accountName = models.account.getAccountName(account)
sourceAccountIdentifier =
accountName !== account._id ? accountName : null
}
}
} else if (!sourceAccountIdentifier && bill.invoice) {
const fileId = bill.invoice?.split(':').pop()
const file = billsFilesMap.get(fileId)
if (file?.cozyMetadata?.sourceAccountIdentifier) {
sourceAccountIdentifier = file.cozyMetadata.sourceAccountIdentifier
} else if (file.cozyMetadata?.sourceAccount) {
const account = billsAndFilesAccountsMap.get(
file.cozyMetadata.sourceAccount
)
if (account) {
if (account.cozyMetadata?.sourceAccountIdentifier) {
sourceAccountIdentifier =
account.cozyMetadata.sourceAccountIdentifier
} else {
const accountName = models.account.getAccountName(account)
sourceAccountIdentifier =
accountName !== account._id ? accountName : null
}
}
}
}
if (!sourceAccountIdentifier) {
return false
}
return {
...bill,
cozyMetadata: { ...bill.cozyMetadata, sourceAccountIdentifier }
}
})
.filter(Boolean)

log('info', `Normalized ${normalizedBills.length}/${bills.length}`)
if (normalizedBills.length > 0) {
await client.saveAll(normalizedBills)
}
}

export async function normalizeAccounts(client) {
const accounts = await client.queryAll(
Q('io.cozy.accounts').partialIndex({
'cozyMetadata.sourceAccountIdentifier': { $exists: false }
})
)

const normalizedAccounts = accounts
.map(account => {
const accountName = models.account.getAccountName(account)
const sourceAccountIdentifier =
accountName !== account._id ? accountName : null
if (!sourceAccountIdentifier) {
return false
}

return {
...account,
cozyMetadata: { ...account.cozyMetadata, sourceAccountIdentifier }
}
})
.filter(Boolean)

log('info', `Normalized ${normalizedAccounts.length}/${accounts.length}`)
if (normalizedAccounts.length > 0) {
await client.saveAll(normalizedAccounts)
}
}
Loading

0 comments on commit 10e2a51

Please sign in to comment.