mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
Bitrix mass import (#2581)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
90b4ac39fb
commit
fe6ee5e99f
@ -80,7 +80,9 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
attachedTo,
|
||||
space,
|
||||
collection,
|
||||
this.txFactory.createTxCreateDoc<P>(_class, space, attributes as unknown as Data<P>, id, modifiedOn, modifiedBy)
|
||||
this.txFactory.createTxCreateDoc<P>(_class, space, attributes as unknown as Data<P>, id, modifiedOn, modifiedBy),
|
||||
modifiedOn,
|
||||
modifiedBy
|
||||
)
|
||||
await this.client.tx(tx)
|
||||
return tx.tx.objectId as unknown as Ref<P>
|
||||
@ -103,7 +105,9 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
attachedTo,
|
||||
space,
|
||||
collection,
|
||||
this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy)
|
||||
this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy),
|
||||
modifiedOn,
|
||||
modifiedBy
|
||||
)
|
||||
await this.client.tx(tx)
|
||||
return tx.objectId
|
||||
@ -124,7 +128,9 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
attachedTo,
|
||||
space,
|
||||
collection,
|
||||
this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy)
|
||||
this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy),
|
||||
modifiedOn,
|
||||
modifiedBy
|
||||
)
|
||||
await this.client.tx(tx)
|
||||
return tx.objectId
|
||||
|
@ -304,7 +304,13 @@ export abstract class TxProcessor implements WithTx {
|
||||
|
||||
static updateDoc2Doc<T extends Doc>(rawDoc: T, tx: TxUpdateDoc<T>): T {
|
||||
const doc = _toDoc(rawDoc)
|
||||
const ops = tx.operations as any
|
||||
TxProcessor.applyUpdate<T>(doc, tx.operations as any)
|
||||
doc.modifiedBy = tx.modifiedBy
|
||||
doc.modifiedOn = tx.modifiedOn
|
||||
return rawDoc
|
||||
}
|
||||
|
||||
static applyUpdate<T extends Doc>(doc: T, ops: any): void {
|
||||
for (const key in ops) {
|
||||
if (key.startsWith('$')) {
|
||||
const operator = _getOperator(key)
|
||||
@ -313,9 +319,6 @@ export abstract class TxProcessor implements WithTx {
|
||||
setObjectValue(key, doc, ops[key])
|
||||
}
|
||||
}
|
||||
doc.modifiedBy = tx.modifiedBy
|
||||
doc.modifiedOn = tx.modifiedOn
|
||||
return rawDoc
|
||||
}
|
||||
|
||||
static updateMixin4Doc<D extends Doc, M extends D>(rawDoc: D, tx: TxMixin<D, M>): D {
|
||||
|
@ -22,7 +22,8 @@
|
||||
core.class.TypeDate,
|
||||
core.class.TypeNumber,
|
||||
core.class.EnumOf,
|
||||
core.class.Collection
|
||||
core.class.Collection,
|
||||
core.class.ArrOf
|
||||
])
|
||||
|
||||
function addMapping (evt: MouseEvent, kind: MappingOperation): void {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { BitrixEntityMapping, BitrixFieldMapping, MappingOperation } from '@hcengineering/bitrix'
|
||||
import { AnyAttribute } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Icon, IconArrowLeft, Label } from '@hcengineering/ui'
|
||||
import { Button, Icon, IconArrowLeft, IconClose, Label } from '@hcengineering/ui'
|
||||
import CopyMappingPresenter from './mappings/CopyMappingPresenter.svelte'
|
||||
import CreateChannelMappingPresenter from './mappings/CreateChannelMappingPresenter.svelte'
|
||||
import CreateTagMappingPresenter from './mappings/CreateTagMappingPresenter.svelte'
|
||||
@ -12,7 +12,12 @@
|
||||
export let value: BitrixFieldMapping
|
||||
$: kind = value.operation.kind
|
||||
|
||||
const attr: AnyAttribute | undefined = getClient().getHierarchy().getAttribute(value.ofClass, value.attributeName)
|
||||
let attr: AnyAttribute | undefined
|
||||
try {
|
||||
attr = getClient().getHierarchy().getAttribute(value.ofClass, value.attributeName)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center top-divider">
|
||||
@ -33,5 +38,13 @@
|
||||
{:else if kind === MappingOperation.DownloadAttachment}
|
||||
<DownloadAttachmentPresenter {mapping} {value} />
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
icon={IconClose}
|
||||
size={'small'}
|
||||
on:click={() => {
|
||||
getClient().remove(value)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hcengineering/bitrix",
|
||||
"version": "0.6.10",
|
||||
"version": "0.6.15",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
|
@ -10,16 +10,21 @@ import core, {
|
||||
Data,
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
FindResult,
|
||||
generateId,
|
||||
Hierarchy,
|
||||
Mixin,
|
||||
MixinUpdate,
|
||||
Ref,
|
||||
Space,
|
||||
TxOperations,
|
||||
TxProcessor,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import tags, { TagElement } from '@hcengineering/tags'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { BitrixClient } from './client'
|
||||
import bitrix from './index'
|
||||
import {
|
||||
BitrixActivity,
|
||||
BitrixEntityMapping,
|
||||
@ -29,127 +34,134 @@ import {
|
||||
LoginInfo
|
||||
} from './types'
|
||||
import { convert, ConvertResult } from './utils'
|
||||
import bitrix from './index'
|
||||
|
||||
async function updateDoc (client: ApplyOperations, doc: Doc, raw: Doc | Data<Doc>): Promise<void> {
|
||||
async function updateDoc (client: ApplyOperations, doc: Doc, raw: Doc | Data<Doc>): Promise<Doc> {
|
||||
// We need to update fields if they are different.
|
||||
const documentUpdate: DocumentUpdate<Doc> = {}
|
||||
for (const [k, v] of Object.entries(raw)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space'].includes(k)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
if (!deepEqual((doc as any)[k], v)) {
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && dv != null && v != null) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
if (Object.keys(documentUpdate).length > 0) {
|
||||
await client.update(doc, documentUpdate)
|
||||
TxProcessor.applyUpdate(doc, documentUpdate)
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
async function updateMixin (
|
||||
client: ApplyOperations,
|
||||
doc: Doc,
|
||||
raw: Doc | Data<Doc>,
|
||||
mixin: Ref<Class<Mixin<Doc>>>
|
||||
): Promise<Doc> {
|
||||
// We need to update fields if they are different.
|
||||
const documentUpdate: MixinUpdate<Doc, Doc> = {}
|
||||
for (const [k, v] of Object.entries(raw)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && dv != null && v != null) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
if (Object.keys(documentUpdate).length > 0) {
|
||||
await client.updateMixin(doc._id, doc._class, doc.space, mixin, documentUpdate)
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function syncPlatform (
|
||||
export async function syncDocument (
|
||||
client: TxOperations,
|
||||
mapping: BitrixEntityMapping,
|
||||
documents: ConvertResult[],
|
||||
existing: Doc | undefined,
|
||||
resultDoc: ConvertResult,
|
||||
info: LoginInfo,
|
||||
frontUrl: string,
|
||||
monitor?: (doc: ConvertResult) => void
|
||||
): Promise<void> {
|
||||
const existingDocuments = await client.findAll(mapping.ofClass, {
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: documents.map((it) => it.document.bitrixId) }
|
||||
})
|
||||
const hierarchy = client.getHierarchy()
|
||||
let syncronized = 0
|
||||
for (const d of documents) {
|
||||
try {
|
||||
const existing = existingDocuments.find(
|
||||
(it) => hierarchy.as<Doc, BitrixSyncDoc>(it, bitrix.mixin.BitrixSyncDoc).bitrixId === d.document.bitrixId
|
||||
|
||||
try {
|
||||
const applyOp = client.apply('bitrix')
|
||||
// const newDoc = existing === undefined
|
||||
existing = await updateMainDoc(applyOp)
|
||||
|
||||
const mixins = { ...resultDoc.mixins }
|
||||
|
||||
// Add bitrix sync mixin
|
||||
mixins[bitrix.mixin.BitrixSyncDoc] = {
|
||||
type: resultDoc.document.type,
|
||||
bitrixId: resultDoc.document.bitrixId,
|
||||
rawData: resultDoc.rawData,
|
||||
syncTime: Date.now()
|
||||
}
|
||||
|
||||
// Check and update mixins
|
||||
await updateMixins(mixins, hierarchy, existing, applyOp, resultDoc.document)
|
||||
|
||||
// Just create supplier documents, like TagElements.
|
||||
for (const ed of resultDoc.extraDocs) {
|
||||
await applyOp.createDoc(
|
||||
ed._class,
|
||||
ed.space,
|
||||
ed,
|
||||
ed._id,
|
||||
resultDoc.document.modifiedOn,
|
||||
resultDoc.document.modifiedBy
|
||||
)
|
||||
const applyOp = client.apply('bitrix')
|
||||
if (existing !== undefined) {
|
||||
// We need update doucment id.
|
||||
d.document._id = existing._id as Ref<BitrixSyncDoc>
|
||||
// We need to update fields if they are different.
|
||||
await updateDoc(applyOp, existing, d.document)
|
||||
}
|
||||
|
||||
// Check and update mixins
|
||||
for (const [m, mv] of Object.entries(d.mixins)) {
|
||||
const mRef = m as Ref<Mixin<Doc>>
|
||||
if (hierarchy.hasMixin(existing, mRef)) {
|
||||
await applyOp.createMixin(
|
||||
d.document._id,
|
||||
d.document._class,
|
||||
d.document.space,
|
||||
m as Ref<Mixin<Doc>>,
|
||||
mv,
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
)
|
||||
} else {
|
||||
const existingM = hierarchy.as(existing, mRef)
|
||||
await updateDoc(applyOp, existingM, mv)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await applyOp.createDoc(
|
||||
d.document._class,
|
||||
d.document.space,
|
||||
d.document,
|
||||
d.document._id,
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
)
|
||||
// Find all attachemnt documents to existing.
|
||||
const byClass = new Map<Ref<Class<Doc>>, (AttachedDoc & BitrixSyncDoc)[]>()
|
||||
|
||||
await applyOp.createMixin<Doc, BitrixSyncDoc>(
|
||||
d.document._id,
|
||||
d.document._class,
|
||||
d.document.space,
|
||||
bitrix.mixin.BitrixSyncDoc,
|
||||
{
|
||||
type: d.document.type,
|
||||
bitrixId: d.document.bitrixId
|
||||
},
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
)
|
||||
for (const [m, mv] of Object.entries(d.mixins)) {
|
||||
await applyOp.createMixin(
|
||||
d.document._id,
|
||||
d.document._class,
|
||||
d.document.space,
|
||||
m as Ref<Mixin<Doc>>,
|
||||
mv,
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
for (const d of resultDoc.extraSync) {
|
||||
byClass.set(d._class, [...(byClass.get(d._class) ?? []), d])
|
||||
}
|
||||
|
||||
for (const [cl, vals] of byClass.entries()) {
|
||||
if (applyOp.getHierarchy().isDerived(cl, core.class.AttachedDoc)) {
|
||||
const existingByClass = await client.findAll(cl, {
|
||||
attachedTo: resultDoc.document._id,
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: vals.map((it) => it.bitrixId) }
|
||||
})
|
||||
|
||||
for (const valValue of vals) {
|
||||
const existing = existingByClass.find(
|
||||
(it) => hierarchy.as<Doc, BitrixSyncDoc>(it, bitrix.mixin.BitrixSyncDoc).bitrixId === valValue.bitrixId
|
||||
)
|
||||
await updateAttachedDoc(existing, applyOp, valValue)
|
||||
}
|
||||
for (const ed of d.extraDocs) {
|
||||
if (applyOp.getHierarchy().isDerived(ed._class, core.class.AttachedDoc)) {
|
||||
const adoc = ed as AttachedDoc
|
||||
await applyOp.addCollection(
|
||||
adoc._class,
|
||||
adoc.space,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
adoc,
|
||||
adoc._id,
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
)
|
||||
} else {
|
||||
await applyOp.createDoc(ed._class, ed.space, ed, ed._id, d.document.modifiedOn, d.document.modifiedBy)
|
||||
}
|
||||
}
|
||||
|
||||
const existingBlobs = await client.findAll(attachment.class.Attachment, {
|
||||
attachedTo: resultDoc.document._id,
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: resultDoc.blobs.map((it) => it[0].bitrixId) }
|
||||
})
|
||||
for (const [ed, op] of resultDoc.blobs) {
|
||||
const existing = existingBlobs.find(
|
||||
(it) => hierarchy.as<Doc, BitrixSyncDoc>(it, bitrix.mixin.BitrixSyncDoc).bitrixId === ed.bitrixId
|
||||
)
|
||||
// For Attachments, just do it once per attachment and assume it is not changed.
|
||||
if (existing === undefined) {
|
||||
const attachmentId: Ref<Attachment> = generateId()
|
||||
try {
|
||||
const edData = await op()
|
||||
if (edData === undefined) {
|
||||
console.error('Failed to retrieve document data', ed.name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for (const ed of d.blobs) {
|
||||
const attachmentId: Ref<Attachment> = generateId()
|
||||
|
||||
const data = new FormData()
|
||||
data.append('file', ed)
|
||||
data.append('file', edData)
|
||||
const resp = await fetch(concatLink(frontUrl, '/files'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -162,76 +174,129 @@ export async function syncPlatform (
|
||||
|
||||
await applyOp.addCollection(
|
||||
attachment.class.Attachment,
|
||||
d.document.space,
|
||||
d.document._id,
|
||||
d.document._class,
|
||||
resultDoc.document.space,
|
||||
resultDoc.document._id,
|
||||
resultDoc.document._class,
|
||||
'attachments',
|
||||
{
|
||||
file: uuid,
|
||||
lastModified: ed.lastModified,
|
||||
name: ed.name,
|
||||
size: ed.size,
|
||||
type: ed.type
|
||||
lastModified: edData.lastModified,
|
||||
name: edData.name,
|
||||
size: edData.size,
|
||||
type: edData.type
|
||||
},
|
||||
attachmentId,
|
||||
d.document.modifiedOn,
|
||||
d.document.modifiedBy
|
||||
resultDoc.document.modifiedOn,
|
||||
resultDoc.document.modifiedBy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (d.comments !== undefined) {
|
||||
const comments = await d.comments
|
||||
if (comments !== undefined && comments.length > 0) {
|
||||
const existingComments = await client.findAll(chunter.class.Comment, {
|
||||
attachedTo: d.document._id,
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: comments.map((it) => it.bitrixId) }
|
||||
})
|
||||
|
||||
for (const comment of comments) {
|
||||
const existing = existingComments.find(
|
||||
(it) => hierarchy.as<Doc, BitrixSyncDoc>(it, bitrix.mixin.BitrixSyncDoc).bitrixId === comment.bitrixId
|
||||
)
|
||||
if (existing !== undefined) {
|
||||
// We need to update fields if they are different.
|
||||
await updateDoc(applyOp, existing, comment)
|
||||
} else {
|
||||
await applyOp.addCollection(
|
||||
comment._class,
|
||||
comment.space,
|
||||
comment.attachedTo,
|
||||
comment.attachedToClass,
|
||||
comment.collection,
|
||||
comment,
|
||||
comment._id,
|
||||
comment.modifiedOn,
|
||||
comment.modifiedBy
|
||||
)
|
||||
|
||||
await applyOp.createMixin<Doc, BitrixSyncDoc>(
|
||||
comment._id,
|
||||
comment._class,
|
||||
comment.space,
|
||||
bitrix.mixin.BitrixSyncDoc,
|
||||
{
|
||||
type: d.document.type,
|
||||
bitrixId: d.document.bitrixId
|
||||
},
|
||||
comment.modifiedOn,
|
||||
comment.modifiedBy
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
await applyOp.commit()
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
console.log('SYNC:', syncronized, documents.length - syncronized)
|
||||
syncronized++
|
||||
monitor?.(d)
|
||||
await applyOp.commit()
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
monitor?.(resultDoc)
|
||||
|
||||
async function updateAttachedDoc (
|
||||
existing: WithLookup<Doc> | undefined,
|
||||
applyOp: ApplyOperations,
|
||||
valValue: AttachedDoc & BitrixSyncDoc
|
||||
): Promise<void> {
|
||||
if (existing !== undefined) {
|
||||
// We need to update fields if they are different.
|
||||
existing = await updateDoc(applyOp, existing, valValue)
|
||||
const existingM = hierarchy.as(existing, bitrix.mixin.BitrixSyncDoc)
|
||||
await updateMixin(
|
||||
applyOp,
|
||||
existingM,
|
||||
{
|
||||
type: valValue.type,
|
||||
bitrixId: valValue.bitrixId,
|
||||
rawData: valValue.rawData
|
||||
},
|
||||
bitrix.mixin.BitrixSyncDoc
|
||||
)
|
||||
} else {
|
||||
const { bitrixId, rawData, ...data } = valValue
|
||||
await applyOp.addCollection<Doc, AttachedDoc>(
|
||||
valValue._class,
|
||||
valValue.space,
|
||||
valValue.attachedTo,
|
||||
valValue.attachedToClass,
|
||||
valValue.collection,
|
||||
data,
|
||||
valValue._id,
|
||||
valValue.modifiedOn,
|
||||
valValue.modifiedBy
|
||||
)
|
||||
|
||||
await applyOp.createMixin<Doc, BitrixSyncDoc>(
|
||||
valValue._id,
|
||||
valValue._class,
|
||||
valValue.space,
|
||||
bitrix.mixin.BitrixSyncDoc,
|
||||
{
|
||||
type: valValue.type,
|
||||
bitrixId: valValue.bitrixId,
|
||||
rawData: valValue.rawData
|
||||
},
|
||||
valValue.modifiedOn,
|
||||
valValue.modifiedBy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMainDoc (applyOp: ApplyOperations): Promise<BitrixSyncDoc> {
|
||||
if (existing !== undefined) {
|
||||
// We need update doucment id.
|
||||
resultDoc.document._id = existing._id as Ref<BitrixSyncDoc>
|
||||
// We need to update fields if they are different.
|
||||
return (await updateDoc(applyOp, existing, resultDoc.document)) as BitrixSyncDoc
|
||||
// Go over extra documents.
|
||||
} else {
|
||||
const { bitrixId, rawData, ...data } = resultDoc.document
|
||||
const id = await applyOp.createDoc<Doc>(
|
||||
resultDoc.document._class,
|
||||
resultDoc.document.space,
|
||||
data,
|
||||
resultDoc.document._id,
|
||||
resultDoc.document.modifiedOn,
|
||||
resultDoc.document.modifiedBy
|
||||
)
|
||||
resultDoc.document._id = id as Ref<BitrixSyncDoc>
|
||||
|
||||
return resultDoc.document
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMixins (
|
||||
mixins: Record<Ref<Mixin<Doc>>, Data<Doc>>,
|
||||
hierarchy: Hierarchy,
|
||||
existing: Doc,
|
||||
applyOp: ApplyOperations,
|
||||
resultDoc: BitrixSyncDoc
|
||||
): Promise<void> {
|
||||
for (const [m, mv] of Object.entries(mixins)) {
|
||||
const mRef = m as Ref<Mixin<Doc>>
|
||||
if (!hierarchy.hasMixin(existing, mRef)) {
|
||||
await applyOp.createMixin(
|
||||
resultDoc._id,
|
||||
resultDoc._class,
|
||||
resultDoc.space,
|
||||
m as Ref<Mixin<Doc>>,
|
||||
mv,
|
||||
resultDoc.modifiedOn,
|
||||
resultDoc.modifiedBy
|
||||
)
|
||||
} else {
|
||||
const existingM = hierarchy.as(existing, mRef)
|
||||
await updateMixin(applyOp, existingM, mv, mRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,6 +353,8 @@ export function processComment (comment: string): string {
|
||||
return comment
|
||||
}
|
||||
|
||||
// 1 day
|
||||
const syncPeriod = 1000 * 60 * 60 * 24
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -312,6 +379,200 @@ export async function performSynchronization (ops: {
|
||||
const userList = new Map<string, Ref<EmployeeAccount>>()
|
||||
|
||||
// Fill all users and create new ones, if required.
|
||||
await synchronizeUsers(userList, ops, allEmployee)
|
||||
|
||||
try {
|
||||
if (ops.space === undefined || ops.mapping.$lookup?.fields === undefined) {
|
||||
return
|
||||
}
|
||||
let processed = 0
|
||||
|
||||
let added = 0
|
||||
|
||||
const sel = ['*', 'UF_*']
|
||||
if (ops.mapping.type === BitrixEntityType.Lead) {
|
||||
sel.push('EMAIL')
|
||||
sel.push('IM')
|
||||
}
|
||||
|
||||
const allTagElements = await ops.client.findAll<TagElement>(tags.class.TagElement, {})
|
||||
|
||||
while (added < ops.limit) {
|
||||
const result = await ops.bitrixClient.call(ops.mapping.type + '.list', {
|
||||
select: sel,
|
||||
order: { ID: ops.direction },
|
||||
start: processed
|
||||
})
|
||||
|
||||
const fields = ops.mapping.$lookup?.fields as BitrixFieldMapping[]
|
||||
|
||||
const toProcess = result.result as any[]
|
||||
const syncTime = Date.now()
|
||||
|
||||
const existingDocuments = await ops.client.findAll<Doc>(ops.mapping.ofClass, {
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: toProcess.map((it) => `${it.ID as string}`) }
|
||||
})
|
||||
const defaultCategories = await ops.client.findAll(tags.class.TagCategory, {
|
||||
default: true
|
||||
})
|
||||
let synchronized = 0
|
||||
while (toProcess.length > 0) {
|
||||
console.log('LOAD:', synchronized, added)
|
||||
synchronized++
|
||||
const [r] = toProcess.slice(0, 1)
|
||||
|
||||
const existingDoc = existingDocuments.find(
|
||||
(it) => ops.client.getHierarchy().as(it, bitrix.mixin.BitrixSyncDoc).bitrixId === r.ID
|
||||
)
|
||||
if (existingDoc !== undefined) {
|
||||
const bd = ops.client.getHierarchy().as(existingDoc, bitrix.mixin.BitrixSyncDoc)
|
||||
if (bd.syncTime !== undefined && bd.syncTime + syncPeriod > syncTime) {
|
||||
// No need to sync, sime sync time is not yet arrived.
|
||||
toProcess.splice(0, 1)
|
||||
added++
|
||||
ops.monitor?.(result.total)
|
||||
if (added >= ops.limit) {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Convert documents.
|
||||
try {
|
||||
const res = await convert(
|
||||
ops.client,
|
||||
ops.mapping,
|
||||
ops.space,
|
||||
fields,
|
||||
r,
|
||||
userList,
|
||||
existingDoc,
|
||||
defaultCategories,
|
||||
allTagElements,
|
||||
ops.blobProvider
|
||||
)
|
||||
|
||||
if (ops.mapping.comments) {
|
||||
await downloadComments(res, ops, commentFieldKeys, userList)
|
||||
}
|
||||
|
||||
added++
|
||||
const total = result.total
|
||||
await syncDocument(ops.client, existingDoc, res, ops.loginInfo, ops.frontUrl, () => {
|
||||
ops.monitor?.(total)
|
||||
})
|
||||
for (const d of res.extraDocs) {
|
||||
// update tags if required
|
||||
if (d._class === tags.class.TagElement) {
|
||||
allTagElements.push(d as TagElement)
|
||||
}
|
||||
}
|
||||
if (added >= ops.limit) {
|
||||
break
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log('failed to obtain data for', r, err)
|
||||
await new Promise((resolve) => {
|
||||
// Sleep for a while
|
||||
setTimeout(resolve, 1000)
|
||||
})
|
||||
}
|
||||
toProcess.splice(0, 1)
|
||||
}
|
||||
|
||||
processed = result.next
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
async function downloadComments (
|
||||
res: ConvertResult,
|
||||
ops: {
|
||||
client: TxOperations
|
||||
bitrixClient: BitrixClient
|
||||
space: Ref<Space> | undefined
|
||||
mapping: WithLookup<BitrixEntityMapping>
|
||||
limit: number
|
||||
direction: 'ASC' | 'DSC'
|
||||
frontUrl: string
|
||||
loginInfo: LoginInfo
|
||||
monitor: (total: number) => void
|
||||
blobProvider?: ((blobRef: any) => Promise<Blob | undefined>) | undefined
|
||||
},
|
||||
commentFieldKeys: string[],
|
||||
userList: Map<string, Ref<EmployeeAccount>>
|
||||
): Promise<void> {
|
||||
const commentsData = await ops.bitrixClient.call(BitrixEntityType.Comment + '.list', {
|
||||
filter: {
|
||||
ENTITY_ID: res.document.bitrixId,
|
||||
ENTITY_TYPE: ops.mapping.type.replace('crm.', '')
|
||||
},
|
||||
select: commentFieldKeys,
|
||||
order: { ID: ops.direction }
|
||||
})
|
||||
for (const it of commentsData.result) {
|
||||
const c: Comment & { bitrixId: string, type: string } = {
|
||||
_id: generateId(),
|
||||
_class: chunter.class.Comment,
|
||||
message: processComment(it.COMMENT as string),
|
||||
bitrixId: it.ID,
|
||||
type: it.ENTITY_TYPE,
|
||||
attachedTo: res.document._id,
|
||||
attachedToClass: res.document._class,
|
||||
collection: 'comments',
|
||||
space: res.document.space,
|
||||
modifiedBy: userList.get(it.AUTHOR_ID) ?? core.account.System,
|
||||
modifiedOn: new Date(it.CREATED ?? new Date().toString()).getTime()
|
||||
}
|
||||
res.extraSync.push(c)
|
||||
}
|
||||
const communications = await ops.bitrixClient.call('crm.activity.list', {
|
||||
order: { ID: 'DESC' },
|
||||
filter: {
|
||||
OWNER_ID: res.document.bitrixId
|
||||
},
|
||||
select: ['*', 'COMMUNICATIONS']
|
||||
})
|
||||
const cr = Array.isArray(communications.result)
|
||||
? (communications.result as BitrixActivity[])
|
||||
: [communications.result as BitrixActivity]
|
||||
for (const comm of cr) {
|
||||
const c: Comment & { bitrixId: string, type: string } = {
|
||||
_id: generateId(),
|
||||
_class: chunter.class.Comment,
|
||||
message: `e-mail:<br/>
|
||||
Subject: ${comm.SUBJECT}
|
||||
${comm.DESCRIPTION}`,
|
||||
bitrixId: comm.ID,
|
||||
type: 'email',
|
||||
attachedTo: res.document._id,
|
||||
attachedToClass: res.document._class,
|
||||
collection: 'comments',
|
||||
space: res.document.space,
|
||||
modifiedBy: userList.get(comm.AUTHOR_ID) ?? core.account.System,
|
||||
modifiedOn: new Date(comm.CREATED ?? new Date().toString()).getTime()
|
||||
}
|
||||
res.extraSync.push(c)
|
||||
}
|
||||
}
|
||||
|
||||
async function synchronizeUsers (
|
||||
userList: Map<string, Ref<EmployeeAccount>>,
|
||||
ops: {
|
||||
client: TxOperations
|
||||
bitrixClient: BitrixClient
|
||||
space: Ref<Space> | undefined
|
||||
mapping: WithLookup<BitrixEntityMapping>
|
||||
limit: number
|
||||
direction: 'ASC' | 'DSC'
|
||||
frontUrl: string
|
||||
loginInfo: LoginInfo
|
||||
monitor: (total: number) => void
|
||||
blobProvider?: ((blobRef: any) => Promise<Blob | undefined>) | undefined
|
||||
},
|
||||
allEmployee: FindResult<EmployeeAccount>
|
||||
): Promise<void> {
|
||||
let totalUsers = 1
|
||||
let next = 0
|
||||
while (userList.size < totalUsers) {
|
||||
@ -337,152 +598,4 @@ export async function performSynchronization (ops: {
|
||||
userList.set(u.ID, accountId)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (ops.space === undefined || ops.mapping.$lookup?.fields === undefined) {
|
||||
return
|
||||
}
|
||||
let processed = 0
|
||||
const tagElements: Map<Ref<Class<Doc>>, TagElement[]> = new Map()
|
||||
|
||||
let added = 0
|
||||
|
||||
while (added < ops.limit) {
|
||||
const sel = ['*', 'UF_*']
|
||||
if (ops.mapping.type === BitrixEntityType.Lead) {
|
||||
sel.push('EMAIL')
|
||||
sel.push('IM')
|
||||
}
|
||||
const result = await ops.bitrixClient.call(ops.mapping.type + '.list', {
|
||||
select: sel,
|
||||
order: { ID: ops.direction },
|
||||
start: processed
|
||||
})
|
||||
|
||||
const extraDocs: Doc[] = []
|
||||
|
||||
const fields = ops.mapping.$lookup?.fields as BitrixFieldMapping[]
|
||||
|
||||
const toProcess = result.result as any[]
|
||||
|
||||
const existingDocuments = await ops.client.findAll(
|
||||
ops.mapping.ofClass,
|
||||
{
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: { $in: toProcess.map((it) => `${it.ID as string}`) }
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
_id: 1,
|
||||
[bitrix.mixin.BitrixSyncDoc + '.bitrixId']: 1
|
||||
}
|
||||
}
|
||||
)
|
||||
const defaultCategories = await ops.client.findAll(tags.class.TagCategory, {
|
||||
default: true
|
||||
})
|
||||
let synchronized = 0
|
||||
while (toProcess.length > 0) {
|
||||
const convertResults: ConvertResult[] = []
|
||||
|
||||
console.log('LOAD:', synchronized, added)
|
||||
synchronized++
|
||||
const [r] = toProcess.slice(0, 1)
|
||||
// Convert documents.
|
||||
try {
|
||||
const res = await convert(
|
||||
ops.client,
|
||||
ops.mapping,
|
||||
ops.space,
|
||||
fields,
|
||||
r,
|
||||
extraDocs,
|
||||
tagElements,
|
||||
userList,
|
||||
existingDocuments,
|
||||
defaultCategories,
|
||||
ops.blobProvider
|
||||
)
|
||||
if (ops.mapping.comments) {
|
||||
res.comments = await ops.bitrixClient
|
||||
.call(BitrixEntityType.Comment + '.list', {
|
||||
filter: {
|
||||
ENTITY_ID: res.document.bitrixId,
|
||||
ENTITY_TYPE: ops.mapping.type.replace('crm.', '')
|
||||
},
|
||||
select: commentFieldKeys,
|
||||
order: { ID: ops.direction }
|
||||
})
|
||||
.then((comments) => {
|
||||
return comments.result.map((it: any) => {
|
||||
const c: Comment & { bitrixId: string, type: string } = {
|
||||
_id: generateId(),
|
||||
_class: chunter.class.Comment,
|
||||
message: processComment(it.COMMENT as string),
|
||||
bitrixId: it.ID,
|
||||
type: it.ENTITY_TYPE,
|
||||
attachedTo: res.document._id,
|
||||
attachedToClass: res.document._class,
|
||||
collection: 'comments',
|
||||
space: res.document.space,
|
||||
modifiedBy: userList.get(it.AUTHOR_ID) ?? core.account.System,
|
||||
modifiedOn: new Date(it.CREATED ?? new Date().toString()).getTime()
|
||||
}
|
||||
return c
|
||||
})
|
||||
})
|
||||
const communications = await ops.bitrixClient.call('crm.activity.list', {
|
||||
order: { ID: 'DESC' },
|
||||
filter: {
|
||||
OWNER_ID: res.document.bitrixId
|
||||
},
|
||||
select: ['*', 'COMMUNICATIONS']
|
||||
})
|
||||
const cr = Array.isArray(communications.result)
|
||||
? (communications.result as BitrixActivity[])
|
||||
: [communications.result as BitrixActivity]
|
||||
for (const comm of cr) {
|
||||
const c: Comment & { bitrixId: string, type: string } = {
|
||||
_id: generateId(),
|
||||
_class: chunter.class.Comment,
|
||||
message: `e-mail:<br/>
|
||||
Subject: ${comm.SUBJECT}
|
||||
${comm.DESCRIPTION}`,
|
||||
bitrixId: comm.ID,
|
||||
type: 'email',
|
||||
attachedTo: res.document._id,
|
||||
attachedToClass: res.document._class,
|
||||
collection: 'comments',
|
||||
space: res.document.space,
|
||||
modifiedBy: userList.get(comm.AUTHOR_ID) ?? core.account.System,
|
||||
modifiedOn: new Date(comm.CREATED ?? new Date().toString()).getTime()
|
||||
}
|
||||
res.comments?.push(c)
|
||||
}
|
||||
}
|
||||
|
||||
convertResults.push(res)
|
||||
extraDocs.push(...res.extraDocs)
|
||||
added++
|
||||
const total = result.total
|
||||
await syncPlatform(ops.client, ops.mapping, convertResults, ops.loginInfo, ops.frontUrl, () => {
|
||||
ops.monitor?.(total)
|
||||
})
|
||||
if (added >= ops.limit) {
|
||||
break
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log('failed to obtain data for', r, err)
|
||||
await new Promise((resolve) => {
|
||||
// Sleep for a while
|
||||
setTimeout(resolve, 1000)
|
||||
})
|
||||
}
|
||||
toProcess.splice(0, 1)
|
||||
}
|
||||
|
||||
processed = result.next
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
@ -86,8 +86,11 @@ export interface LoginInfo {
|
||||
* @public
|
||||
*/
|
||||
export interface BitrixSyncDoc extends Doc {
|
||||
type: string
|
||||
type?: string
|
||||
bitrixId: string
|
||||
syncTime?: number
|
||||
// raw bitrix document data.
|
||||
rawData?: any
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,18 @@
|
||||
import { Comment } from '@hcengineering/chunter'
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import contact, { Channel, EmployeeAccount } from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, Class, Client, Data, Doc, generateId, Mixin, Ref, Space } from '@hcengineering/core'
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Client,
|
||||
Data,
|
||||
Doc,
|
||||
generateId,
|
||||
Mixin,
|
||||
Ref,
|
||||
Space,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import tags, { TagCategory, TagElement, TagReference } from '@hcengineering/tags'
|
||||
import {
|
||||
BitrixEntityMapping,
|
||||
@ -12,7 +24,6 @@ import {
|
||||
DownloadAttachmentOperation,
|
||||
MappingOperation
|
||||
} from '.'
|
||||
import bitrix from './index'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -42,10 +53,11 @@ export function collectFields (fieldMapping: BitrixFieldMapping[]): string[] {
|
||||
*/
|
||||
export interface ConvertResult {
|
||||
document: BitrixSyncDoc // Document we should achive
|
||||
rawData: any
|
||||
mixins: Record<Ref<Mixin<Doc>>, Data<Doc>> // Mixins of document we will achive
|
||||
extraDocs: Doc[] // Extra documents we will achive, comments etc.
|
||||
blobs: File[] //
|
||||
comments?: Array<BitrixSyncDoc & Comment>
|
||||
extraDocs: Doc[] // Extra documents we will achive, etc.
|
||||
extraSync: (AttachedDoc & BitrixSyncDoc)[] // Extra documents we will achive, etc.
|
||||
blobs: [Attachment & BitrixSyncDoc, () => Promise<File | undefined>][]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,11 +69,10 @@ export async function convert (
|
||||
space: Ref<Space>,
|
||||
fields: BitrixFieldMapping[],
|
||||
rawDocument: any,
|
||||
prevExtra: Doc[], // <<-- a list of previous extra documents, so for example TagElement will be reused, if present for more what one item and required to be created
|
||||
tagElements: Map<Ref<Class<Doc>>, TagElement[]>, // TagElement cache.
|
||||
userList: Map<string, Ref<EmployeeAccount>>,
|
||||
existingDocuments: Doc[],
|
||||
existingDoc: WithLookup<Doc> | undefined,
|
||||
defaultCategories: TagCategory[],
|
||||
allTagElements: TagElement[],
|
||||
blobProvider?: (blobRef: any) => Promise<Blob | undefined>
|
||||
): Promise<ConvertResult> {
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -76,13 +87,13 @@ export async function convert (
|
||||
modifiedBy: userList.get(rawDocument.CREATED_BY_ID) ?? core.account.System
|
||||
}
|
||||
|
||||
const existingId = existingDocuments.find((it) => (it as any)[bitrix.mixin.BitrixSyncDoc].bitrixId === bitrixId)
|
||||
?._id as Ref<BitrixSyncDoc>
|
||||
const existingId = existingDoc?._id
|
||||
|
||||
// Obtain a proper modified by for document
|
||||
|
||||
const newExtraSyncDocs: (AttachedDoc & BitrixSyncDoc)[] = []
|
||||
const newExtraDocs: Doc[] = []
|
||||
const blobs: File[] = []
|
||||
const blobs: [Attachment & BitrixSyncDoc, () => Promise<File | undefined>][] = []
|
||||
const mixins: Record<Ref<Mixin<Doc>>, Data<Doc>> = {}
|
||||
|
||||
const extractValue = (field?: string, alternatives?: string[]): any | undefined => {
|
||||
@ -119,18 +130,32 @@ export async function convert (
|
||||
return lval
|
||||
} else if (bfield.type === 'date') {
|
||||
if (lval !== '' && lval != null) {
|
||||
return new Date(lval)
|
||||
return new Date(lval).getTime()
|
||||
}
|
||||
} else if (bfield.type === 'char') {
|
||||
return lval === 'Y'
|
||||
} else if (bfield.type === 'enumeration' || bfield.type === 'crm_status') {
|
||||
if (lval != null && lval !== '') {
|
||||
if (bfield.isMultiple && Array.isArray(lval)) {
|
||||
lval = lval[0] ?? ''
|
||||
}
|
||||
const eValue = bfield.items?.find((it) => it.ID === lval)?.VALUE
|
||||
if (eValue !== undefined) {
|
||||
return eValue
|
||||
if (bfield.isMultiple) {
|
||||
const results: any[] = []
|
||||
for (let llval of Array.isArray(lval) ? lval : [lval]) {
|
||||
if (typeof llval === 'number') {
|
||||
llval = llval.toString()
|
||||
}
|
||||
const eValue = bfield.items?.find((it) => it.ID === llval)?.VALUE
|
||||
if (eValue !== undefined) {
|
||||
results.push(eValue)
|
||||
}
|
||||
}
|
||||
return results
|
||||
} else {
|
||||
if (typeof lval === 'number') {
|
||||
lval = lval.toString()
|
||||
}
|
||||
const eValue = bfield.items?.find((it) => it.ID === lval)?.VALUE
|
||||
if (eValue !== undefined) {
|
||||
return eValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,15 +170,24 @@ export async function convert (
|
||||
}
|
||||
const lval = extractValue(o.field, o.alternatives)
|
||||
if (lval != null) {
|
||||
r.push(lval)
|
||||
if (Array.isArray(lval)) {
|
||||
r.push(...lval)
|
||||
} else {
|
||||
r.push(lval)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r.length === 1) {
|
||||
return r[0]
|
||||
}
|
||||
if (r.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (attr.type._class === core.class.ArrOf) {
|
||||
return r
|
||||
}
|
||||
if (r.length === 1) {
|
||||
return r[0]
|
||||
}
|
||||
|
||||
return r.join('').trim()
|
||||
}
|
||||
const getDownloadValue = async (attr: AnyAttribute, operation: DownloadAttachmentOperation): Promise<any> => {
|
||||
@ -192,7 +226,7 @@ export async function convert (
|
||||
continue
|
||||
}
|
||||
}
|
||||
const c: Channel = {
|
||||
const c: Channel & BitrixSyncDoc = {
|
||||
_id: generateId(),
|
||||
_class: contact.class.Channel,
|
||||
attachedTo: document._id,
|
||||
@ -202,9 +236,10 @@ export async function convert (
|
||||
value: svalue,
|
||||
provider: f.provider,
|
||||
space: document.space,
|
||||
modifiedOn: document.modifiedOn
|
||||
modifiedOn: document.modifiedOn,
|
||||
bitrixId: svalue
|
||||
}
|
||||
newExtraDocs.push(c)
|
||||
newExtraSyncDocs.push(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,22 +247,6 @@ export async function convert (
|
||||
}
|
||||
|
||||
const getTagValue = async (attr: AnyAttribute, operation: CreateTagOperation): Promise<any> => {
|
||||
const elements =
|
||||
tagElements.get(attr.attributeOf) ??
|
||||
(await client.findAll<TagElement>(tags.class.TagElement, {
|
||||
targetClass: attr.attributeOf
|
||||
}))
|
||||
|
||||
const references =
|
||||
existingId !== undefined
|
||||
? await client.findAll<TagReference>(tags.class.TagReference, {
|
||||
attachedTo: existingId
|
||||
})
|
||||
: []
|
||||
// Add tags creation requests from previous conversions.
|
||||
elements.push(...prevExtra.filter((it) => it._class === tags.class.TagElement).map((it) => it as TagElement))
|
||||
|
||||
tagElements.set(attr.attributeOf, elements)
|
||||
const defaultCategory = defaultCategories.find((it) => it.targetClass === attr.attributeOf)
|
||||
|
||||
if (defaultCategory === undefined) {
|
||||
@ -245,13 +264,14 @@ export async function convert (
|
||||
} else {
|
||||
vals = [lval as string]
|
||||
}
|
||||
let ci = 0
|
||||
for (let vv of vals) {
|
||||
vv = vv.trim()
|
||||
if (vv === '') {
|
||||
continue
|
||||
}
|
||||
// Find existing element and create reference based on it.
|
||||
let tag: TagElement | undefined = elements.find((it) => it.title === vv)
|
||||
let tag: TagElement | undefined = allTagElements.find((it) => it.title === vv)
|
||||
if (tag === undefined) {
|
||||
tag = {
|
||||
_id: generateId(),
|
||||
@ -267,24 +287,22 @@ export async function convert (
|
||||
}
|
||||
newExtraDocs.push(tag)
|
||||
}
|
||||
const ref: TagReference = {
|
||||
const ref: TagReference & BitrixSyncDoc = {
|
||||
_id: generateId(),
|
||||
attachedTo: existingId ?? document._id,
|
||||
attachedToClass: attr.attributeOf,
|
||||
collection: attr.name,
|
||||
_class: tags.class.TagReference,
|
||||
tag: tag._id,
|
||||
color: 1,
|
||||
color: ci++,
|
||||
title: vv,
|
||||
weight: o.weight,
|
||||
modifiedBy: document.modifiedBy,
|
||||
modifiedOn: document.modifiedOn,
|
||||
space: tags.space.Tags
|
||||
}
|
||||
if (references.find((it) => it.title === vv) === undefined) {
|
||||
// Add only if not already added
|
||||
newExtraDocs.push(ref)
|
||||
space: tags.space.Tags,
|
||||
bitrixId: vv
|
||||
}
|
||||
newExtraSyncDocs.push(ref)
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
@ -310,19 +328,45 @@ export async function convert (
|
||||
case MappingOperation.DownloadAttachment: {
|
||||
const blobRef: { file: string, id: string } = await getDownloadValue(attr, f.operation)
|
||||
if (blobRef !== undefined) {
|
||||
const response = await blobProvider?.(blobRef)
|
||||
if (response !== undefined) {
|
||||
let fname = blobRef.id
|
||||
switch (response.type) {
|
||||
case 'application/pdf':
|
||||
fname += '.pdf'
|
||||
break
|
||||
case 'application/msword':
|
||||
fname += '.doc'
|
||||
break
|
||||
}
|
||||
blobs.push(new File([response], fname, { type: response.type }))
|
||||
const attachDoc: Attachment & BitrixSyncDoc = {
|
||||
_id: generateId(),
|
||||
bitrixId: blobRef.id,
|
||||
file: '', // Empty since not uploaded yet.
|
||||
name: blobRef.id,
|
||||
size: -1,
|
||||
type: 'application/octet-stream',
|
||||
lastModified: 0,
|
||||
attachedTo: existingId ?? document._id,
|
||||
attachedToClass: attr.attributeOf,
|
||||
collection: attr.name,
|
||||
_class: attachment.class.Attachment,
|
||||
modifiedBy: document.modifiedBy,
|
||||
modifiedOn: document.modifiedOn,
|
||||
space: document.space
|
||||
}
|
||||
|
||||
blobs.push([
|
||||
attachDoc,
|
||||
async () => {
|
||||
if (blobRef !== undefined) {
|
||||
const response = await blobProvider?.(blobRef)
|
||||
if (response !== undefined) {
|
||||
let fname = blobRef.id
|
||||
switch (response.type) {
|
||||
case 'application/pdf':
|
||||
fname += '.pdf'
|
||||
break
|
||||
case 'application/msword':
|
||||
fname += '.doc'
|
||||
break
|
||||
}
|
||||
attachDoc.type = response.type
|
||||
attachDoc.name = fname
|
||||
return new File([response], fname, { type: response.type })
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -339,7 +383,7 @@ export async function convert (
|
||||
}
|
||||
}
|
||||
|
||||
return { document, mixins, extraDocs: newExtraDocs, blobs }
|
||||
return { document, mixins, extraSync: newExtraSyncDocs, extraDocs: newExtraDocs, blobs, rawData: rawDocument }
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user