From 27b003f35f269a6a0cdbcae0c81d01aa0d36e1de Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 16 Dec 2022 13:35:51 +0700 Subject: [PATCH] Support Bitrix Lead import (#2445) Signed-off-by: Andrey Sobolev --- common/config/rush/pnpm-lock.yaml | 245 ++++++++++- common/scripts/install-run-rush.js | 7 +- common/scripts/install-run.js | 61 ++- dev/prod/package.json | 6 +- dev/prod/src/platform.ts | 4 + models/all/package.json | 3 +- models/all/src/index.ts | 2 + models/all/src/migration.ts | 4 +- models/bitrix/.eslintrc.js | 7 + models/bitrix/.npmignore | 4 + models/bitrix/config/rig.json | 18 + models/bitrix/package.json | 45 ++ models/bitrix/src/index.ts | 99 +++++ models/bitrix/src/migration.ts | 21 + models/bitrix/src/plugin.ts | 37 ++ models/bitrix/tsconfig.json | 8 + packages/core/src/operations.ts | 92 +++-- packages/core/src/tx.ts | 72 +++- .../presentation/src/components/Card.svelte | 2 + .../src/components/ObjectPopup.svelte | 4 +- packages/theme/styles/components.scss | 10 +- packages/theme/styles/dialogs.scss | 4 + .../ui/src/components/DropdownLabels.svelte | 3 +- .../src/components/DropdownLabelsIntl.svelte | 3 +- packages/ui/src/components/EditBox.svelte | 6 +- packages/ui/src/components/Expandable.svelte | 66 +++ packages/ui/src/components/Popup.svelte | 24 +- packages/ui/src/index.ts | 1 + .../src/components/utils.ts | 9 + plugins/bitrix-assets/.eslintrc.js | 7 + plugins/bitrix-assets/assets/icons.svg | 16 + plugins/bitrix-assets/config/rig.json | 5 + plugins/bitrix-assets/lang/en.json | 15 + plugins/bitrix-assets/lang/ru.json | 15 + plugins/bitrix-assets/package.json | 35 ++ .../bitrix-assets/src/__tests__/lang.test.ts | 6 + plugins/bitrix-assets/src/index.ts | 24 ++ plugins/bitrix-assets/tsconfig.json | 18 + plugins/bitrix-resources/.eslintrc.js | 7 + plugins/bitrix-resources/config/rig.json | 5 + plugins/bitrix-resources/package.json | 56 +++ plugins/bitrix-resources/postcss.config.js | 5 + plugins/bitrix-resources/src/client.ts | 28 ++ .../src/components/AttributeMapper.svelte | 102 +++++ .../src/components/BitrixConfigure.svelte | 96 +++++ .../src/components/BitrixConnect.svelte | 44 ++ .../src/components/BitrixFieldLookup.svelte | 142 +++++++ .../src/components/BitrixImport.svelte | 61 +++ .../src/components/CreateMapping.svelte | 67 +++ .../components/CreateMappingAttribute.svelte | 43 ++ .../src/components/EntityMapping.svelte | 167 ++++++++ .../src/components/EnumPopup.svelte | 46 +++ .../components/FieldMappingPresenter.svelte | 34 ++ .../FieldMappingSynchronizer.svelte | 389 ++++++++++++++++++ .../src/components/icons/Bitrix.svelte | 38 ++ .../components/mappings/CopyMapping.svelte | 134 ++++++ .../mappings/CopyMappingPresenter.svelte | 48 +++ .../mappings/CreateChannelMapping.svelte | 116 ++++++ .../CreateChannelMappingPresenter.svelte | 46 +++ .../mappings/CreateTagMapping.svelte | 137 ++++++ .../mappings/CreateTagMappingPresenter.svelte | 49 +++ plugins/bitrix-resources/src/index.ts | 33 ++ plugins/bitrix-resources/src/plugin.ts | 47 +++ plugins/bitrix-resources/src/types.ts | 45 ++ plugins/bitrix-resources/src/utils.ts | 277 +++++++++++++ plugins/bitrix-resources/svelte.config.js | 5 + plugins/bitrix-resources/tsconfig.json | 16 + plugins/bitrix/.eslintrc.js | 7 + plugins/bitrix/.npmignore | 4 + plugins/bitrix/config/rig.json | 18 + plugins/bitrix/package.json | 36 ++ plugins/bitrix/src/index.ts | 184 +++++++++ plugins/bitrix/tsconfig.json | 9 + plugins/setting-assets/lang/en.json | 3 +- plugins/setting-assets/lang/ru.json | 3 +- .../src/components/ClassAttributes.svelte | 28 +- .../src/components/ClassHierarchy.svelte | 7 +- .../src/components/ClassSetting.svelte | 42 +- .../src/components/EditEnum.svelte | 4 +- .../src/components/PluginCard.svelte | 88 ++-- .../typeEditors/EnumTypeEditor.svelte | 5 +- plugins/setting-resources/src/index.ts | 4 +- plugins/setting-resources/src/utils.ts | 47 +++ plugins/setting/src/index.ts | 16 +- plugins/tags-resources/src/index.ts | 2 + .../src/components/ActionsPopup.svelte | 2 + .../src/components/NumberEditor.svelte | 29 +- rush.json | 27 +- server/core/src/storage.ts | 9 +- 89 files changed, 3696 insertions(+), 169 deletions(-) create mode 100644 models/bitrix/.eslintrc.js create mode 100644 models/bitrix/.npmignore create mode 100644 models/bitrix/config/rig.json create mode 100644 models/bitrix/package.json create mode 100644 models/bitrix/src/index.ts create mode 100644 models/bitrix/src/migration.ts create mode 100644 models/bitrix/src/plugin.ts create mode 100644 models/bitrix/tsconfig.json create mode 100644 packages/ui/src/components/Expandable.svelte create mode 100644 plugins/bitrix-assets/.eslintrc.js create mode 100644 plugins/bitrix-assets/assets/icons.svg create mode 100644 plugins/bitrix-assets/config/rig.json create mode 100644 plugins/bitrix-assets/lang/en.json create mode 100644 plugins/bitrix-assets/lang/ru.json create mode 100644 plugins/bitrix-assets/package.json create mode 100644 plugins/bitrix-assets/src/__tests__/lang.test.ts create mode 100644 plugins/bitrix-assets/src/index.ts create mode 100644 plugins/bitrix-assets/tsconfig.json create mode 100644 plugins/bitrix-resources/.eslintrc.js create mode 100644 plugins/bitrix-resources/config/rig.json create mode 100644 plugins/bitrix-resources/package.json create mode 100644 plugins/bitrix-resources/postcss.config.js create mode 100644 plugins/bitrix-resources/src/client.ts create mode 100644 plugins/bitrix-resources/src/components/AttributeMapper.svelte create mode 100644 plugins/bitrix-resources/src/components/BitrixConfigure.svelte create mode 100644 plugins/bitrix-resources/src/components/BitrixConnect.svelte create mode 100644 plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte create mode 100644 plugins/bitrix-resources/src/components/BitrixImport.svelte create mode 100644 plugins/bitrix-resources/src/components/CreateMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte create mode 100644 plugins/bitrix-resources/src/components/EntityMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/EnumPopup.svelte create mode 100644 plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte create mode 100644 plugins/bitrix-resources/src/components/FieldMappingSynchronizer.svelte create mode 100644 plugins/bitrix-resources/src/components/icons/Bitrix.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CopyMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CopyMappingPresenter.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CreateChannelMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CreateChannelMappingPresenter.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CreateTagMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/CreateTagMappingPresenter.svelte create mode 100644 plugins/bitrix-resources/src/index.ts create mode 100644 plugins/bitrix-resources/src/plugin.ts create mode 100644 plugins/bitrix-resources/src/types.ts create mode 100644 plugins/bitrix-resources/src/utils.ts create mode 100644 plugins/bitrix-resources/svelte.config.js create mode 100644 plugins/bitrix-resources/tsconfig.json create mode 100644 plugins/bitrix/.eslintrc.js create mode 100644 plugins/bitrix/.npmignore create mode 100644 plugins/bitrix/config/rig.json create mode 100644 plugins/bitrix/package.json create mode 100644 plugins/bitrix/src/index.ts create mode 100644 plugins/bitrix/tsconfig.json create mode 100644 plugins/setting-resources/src/utils.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 089bc69d0b..e6093a9039 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -17,6 +17,9 @@ specifiers: '@rush-temp/automation': file:./projects/automation.tgz '@rush-temp/automation-assets': file:./projects/automation-assets.tgz '@rush-temp/automation-resources': file:./projects/automation-resources.tgz + '@rush-temp/bitrix': file:./projects/bitrix.tgz + '@rush-temp/bitrix-assets': file:./projects/bitrix-assets.tgz + '@rush-temp/bitrix-resources': file:./projects/bitrix-resources.tgz '@rush-temp/board': file:./projects/board.tgz '@rush-temp/board-assets': file:./projects/board-assets.tgz '@rush-temp/board-resources': file:./projects/board-resources.tgz @@ -70,6 +73,7 @@ specifiers: '@rush-temp/model-all': file:./projects/model-all.tgz '@rush-temp/model-attachment': file:./projects/model-attachment.tgz '@rush-temp/model-automation': file:./projects/model-automation.tgz + '@rush-temp/model-bitrix': file:./projects/model-bitrix.tgz '@rush-temp/model-board': file:./projects/model-board.tgz '@rush-temp/model-calendar': file:./projects/model-calendar.tgz '@rush-temp/model-chunter': file:./projects/model-chunter.tgz @@ -238,6 +242,7 @@ specifiers: '@types/minio': ~7.0.11 '@types/node': ~16.11.12 '@types/pdfkit': ~0.12.3 + '@types/qs': ~6.9.7 '@types/request': ~2.48.8 '@types/sharp': ~0.30.4 '@types/tar-stream': ^2.2.2 @@ -296,6 +301,7 @@ specifiers: mini-css-extract-plugin: ^2.2.0 minio: ^7.0.26 mongodb: ^4.11.0 + p-queue: ~7.3.0 pdfkit: ~0.13.0 postcss: ^8.3.4 postcss-load-config: ^3.1.0 @@ -308,6 +314,7 @@ specifiers: prosemirror-state: ~1.4.1 prosemirror-transform: ~1.7.0 prosemirror-view: ~1.29.0 + qs: ~6.11.0 rfc6902: ~5.0.1 sass: ^1.53.0 sass-loader: ^12.1.0 @@ -354,6 +361,9 @@ dependencies: '@rush-temp/automation': file:projects/automation.tgz '@rush-temp/automation-assets': file:projects/automation-assets.tgz_typescript@4.8.4 '@rush-temp/automation-resources': file:projects/automation-resources.tgz_49b4785992daa3b61a639b2b31601e76 + '@rush-temp/bitrix': file:projects/bitrix.tgz + '@rush-temp/bitrix-assets': file:projects/bitrix-assets.tgz_typescript@4.8.4 + '@rush-temp/bitrix-resources': file:projects/bitrix-resources.tgz_49b4785992daa3b61a639b2b31601e76 '@rush-temp/board': file:projects/board.tgz '@rush-temp/board-assets': file:projects/board-assets.tgz_typescript@4.8.4 '@rush-temp/board-resources': file:projects/board-resources.tgz_49b4785992daa3b61a639b2b31601e76 @@ -407,6 +417,7 @@ dependencies: '@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.8.4 '@rush-temp/model-attachment': file:projects/model-attachment.tgz_typescript@4.8.4 '@rush-temp/model-automation': file:projects/model-automation.tgz_typescript@4.8.4 + '@rush-temp/model-bitrix': file:projects/model-bitrix.tgz_typescript@4.8.4 '@rush-temp/model-board': file:projects/model-board.tgz_typescript@4.8.4 '@rush-temp/model-calendar': file:projects/model-calendar.tgz_typescript@4.8.4 '@rush-temp/model-chunter': file:projects/model-chunter.tgz_typescript@4.8.4 @@ -575,6 +586,7 @@ dependencies: '@types/minio': 7.0.14 '@types/node': 16.11.68 '@types/pdfkit': 0.12.8 + '@types/qs': 6.9.7 '@types/request': 2.48.8 '@types/sharp': 0.30.5 '@types/tar-stream': 2.2.2 @@ -633,6 +645,7 @@ dependencies: mini-css-extract-plugin: 2.6.1_webpack@5.75.0 minio: 7.0.32 mongodb: 4.11.0 + p-queue: 7.3.0 pdfkit: 0.13.0 postcss: 8.4.19 postcss-load-config: 3.1.4_postcss@8.4.19+ts-node@10.9.1 @@ -645,6 +658,7 @@ dependencies: prosemirror-state: 1.4.2 prosemirror-transform: 1.7.0 prosemirror-view: 1.29.0 + qs: 6.11.0 rfc6902: 5.0.1 sass: 1.56.1 sass-loader: 12.6.0_sass@1.56.1+webpack@5.75.0 @@ -676,6 +690,21 @@ dependencies: packages: + /@2bad/bitrix/2.5.0: + resolution: {integrity: sha512-AXlbCsn5NV1x8H982JHk2yyNU/vtTBx34a6DdtqqgHQEYd6nfEVnHEhUF/yAmElV1/rtjubOtKVxwVdFUrwLDA==} + dependencies: + '@types/lodash.chunk': 4.2.7 + '@types/lodash.frompairs': 4.0.7 + '@types/lodash.range': 3.2.7 + '@types/qs': 6.9.7 + got: 11.8.2 + lodash.chunk: 4.2.0 + lodash.frompairs: 4.0.1 + lodash.range: 3.2.0 + p-queue: 6.6.2 + qs: 6.11.0 + dev: false + /@_ueberdosis/prosemirror-tables/1.1.3: resolution: {integrity: sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==} dependencies: @@ -3091,6 +3120,28 @@ packages: '@types/koa': 2.13.5 dev: false + /@types/lodash.chunk/4.2.7: + resolution: {integrity: sha512-//tmaWHiANgToom/YYYKKqiCtlNz11fwYtMUUbaemNSbWTI+2zHtYW5nt1PHNCRWHPAJHHhn4UVFD9LKUFvatA==} + dependencies: + '@types/lodash': 4.14.191 + dev: false + + /@types/lodash.frompairs/4.0.7: + resolution: {integrity: sha512-7UeH2+GF9yop4AqnPwae5/2TE+eY0WRDy0RRQtNGHjzIgdUhilRskMXvXqUcCSazvbkxasjqydXrIE1OB6bPKA==} + dependencies: + '@types/lodash': 4.14.191 + dev: false + + /@types/lodash.range/3.2.7: + resolution: {integrity: sha512-sq3eRZWspMKGAE0QLH+tIE/EWeSEVKlFqaUoqmgR/c4keXiTP8ob24TUzclYKihw/ojftz6vBb632NRmxR4qrQ==} + dependencies: + '@types/lodash': 4.14.191 + dev: false + + /@types/lodash/4.14.191: + resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} + dev: false + /@types/mime-types/2.1.1: resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==} dev: false @@ -6354,6 +6405,23 @@ packages: get-intrinsic: 1.1.3 dev: false + /got/11.8.2: + resolution: {integrity: sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==} + engines: {node: '>=10.19.0'} + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.0 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.2 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + dev: false + /got/11.8.5: resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==} engines: {node: '>=10.19.0'} @@ -7994,10 +8062,18 @@ packages: p-locate: 5.0.0 dev: false + /lodash.chunk/4.2.0: + resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==} + dev: false + /lodash.debounce/4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: false + /lodash.frompairs/4.0.1: + resolution: {integrity: sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==} + dev: false + /lodash.get/4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} dev: false @@ -8010,6 +8086,10 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: false + /lodash.range/3.2.0: + resolution: {integrity: sha512-Fgkb7SinmuzqgIhNhAElo0BL/R1rHCnhwSZf78omqSwvWqD0kD2ssOAutQonDKH/ldS8BxA72ORYI09qAY9CYg==} + dev: false + /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: false @@ -8595,6 +8675,11 @@ packages: engines: {node: '>=8'} dev: false + /p-finally/1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: false + /p-limit/2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -8623,6 +8708,22 @@ packages: p-limit: 3.1.0 dev: false + /p-queue/6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + dev: false + + /p-queue/7.3.0: + resolution: {integrity: sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==} + engines: {node: '>=12'} + dependencies: + eventemitter3: 4.0.7 + p-timeout: 5.1.0 + dev: false + /p-retry/4.6.2: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} @@ -8631,6 +8732,18 @@ packages: retry: 0.13.1 dev: false + /p-timeout/3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: false + + /p-timeout/5.1.0: + resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} + engines: {node: '>=12'} + dev: false + /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -11139,7 +11252,7 @@ packages: dev: false file:projects/account.tgz: - resolution: {integrity: sha512-QSlfOq1M5zdpWg0tNGWBu3VegEZkkPLQG2gim+GXAC7vEQd7vRsWXTMbKntwOdkovV29NDNsIcUWp6G5DUfFRg==, tarball: file:projects/account.tgz} + resolution: {integrity: sha512-Cu4HQ37cZjY4LU7x7/btaVEfxIjb+rKA9hll64ZloH41F54jembIyQHHufIMatcMOnPFivsRZ4P9oj7WMyvOsA==, tarball: file:projects/account.tgz} name: '@rush-temp/account' version: 0.0.0 dependencies: @@ -11422,6 +11535,89 @@ packages: - supports-color dev: false + file:projects/bitrix-assets.tgz_typescript@4.8.4: + resolution: {integrity: sha512-Hod/zdxq3gCpr1vfwff7eAusB6KTImLvny1hk3x5Rcn9Rri0SMTJKm3NY0Ds+XTCnnOFZQscD81EfIpaeQPITw==, tarball: file:projects/bitrix-assets.tgz} + id: file:projects/bitrix-assets.tgz + name: '@rush-temp/bitrix-assets' + version: 0.0.0 + dependencies: + '@rushstack/heft': 0.47.11 + '@types/heft-jest': 1.0.3 + '@types/node': 16.11.68 + '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 + '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + eslint: 8.27.0 + eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566 + eslint-plugin-import: 2.26.0_eslint@8.27.0 + eslint-plugin-n: 15.5.1_eslint@8.27.0 + eslint-plugin-promise: 6.1.1_eslint@8.27.0 + prettier: 2.7.1 + transitivePeerDependencies: + - supports-color + - typescript + dev: false + + file:projects/bitrix-resources.tgz_49b4785992daa3b61a639b2b31601e76: + resolution: {integrity: sha512-QQs9ffRu6ZbMcDsIHbbqwEnqlVIFPT5SOemp5PMLCfLcAhJn4arE89dCCuVLZ6p/JMYF3g7SCy/dgUllg4nNxA==, tarball: file:projects/bitrix-resources.tgz} + id: file:projects/bitrix-resources.tgz + name: '@rush-temp/bitrix-resources' + version: 0.0.0 + dependencies: + '@2bad/bitrix': 2.5.0 + '@types/qs': 6.9.7 + '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 + '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + eslint: 8.27.0 + eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566 + eslint-plugin-import: 2.26.0_eslint@8.27.0 + eslint-plugin-n: 15.5.1_eslint@8.27.0 + eslint-plugin-promise: 6.1.1_eslint@8.27.0 + eslint-plugin-svelte3: 4.0.0_eslint@8.27.0+svelte@3.53.1 + fast-equals: 2.0.4 + p-queue: 7.3.0 + prettier: 2.7.1 + prettier-plugin-svelte: 2.8.0_prettier@2.7.1+svelte@3.53.1 + qs: 6.11.0 + sass: 1.56.1 + svelte: 3.53.1 + svelte-check: 2.9.2_911be9570a99ae2f9e069a41b9f14a00 + svelte-loader: 3.1.4_svelte@3.53.1 + svelte-preprocess: 4.10.7_1cd24d71cb02643c0a6ca17ff2edd158 + typescript: 4.8.4 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - node-sass + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + dev: false + + file:projects/bitrix.tgz: + resolution: {integrity: sha512-fLYL3iRRLeMJbRIjfkylukQ/uDZ/OfDuzAvrVHW/dCu2RIEFSpbYDvTlH4Cu0CR6adruVNSP4E3F+yAZSIHmQg==, tarball: file:projects/bitrix.tgz} + name: '@rush-temp/bitrix' + version: 0.0.0 + dependencies: + '@2bad/bitrix': 2.5.0 + '@rushstack/heft': 0.47.11 + '@types/heft-jest': 1.0.3 + '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 + '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + eslint: 8.27.0 + eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566 + eslint-plugin-import: 2.26.0_eslint@8.27.0 + eslint-plugin-n: 15.5.1_eslint@8.27.0 + eslint-plugin-promise: 6.1.1_eslint@8.27.0 + prettier: 2.7.1 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: false + file:projects/board-assets.tgz_typescript@4.8.4: resolution: {integrity: sha512-rjdJ/U7sW/VdIPr31Kf1TCA9NxfStFMcypFbhhP13V00V4KM15ZzjdS9dcVhR0S1mqrXSomEZqzUeHpjkyfGgA==, tarball: file:projects/board-assets.tgz} id: file:projects/board-assets.tgz @@ -11841,7 +12037,7 @@ packages: dev: false file:projects/dev-client-resources.tgz: - resolution: {integrity: sha512-Bf1K9DyTyiKCNSXwfDfnMEQvyTpwTKLxtermBHCAtH/X07jkRTQR78TityPmMY13qEP29GzVbiLdcYwMStLuoA==, tarball: file:projects/dev-client-resources.tgz} + resolution: {integrity: sha512-aBdWjVRiHsvyjb4ZVwV3CcoYF+LnWbnBtZjvQh0JbVTBZOZfOcXUiDjw73dugHlSu0EqemahiIpKl0B8H40f2g==, tarball: file:projects/dev-client-resources.tgz} name: '@rush-temp/dev-client-resources' version: 0.0.0 dependencies: @@ -11907,7 +12103,7 @@ packages: dev: false file:projects/devmodel-resources.tgz_5536a2d3219f8677582bfc0330dae14a: - resolution: {integrity: sha512-ipNVydhF5HfLSsx/TEb3G3Zo1Cag6f/yyDcWDXZfI73extFwzmdObyiPFPvXeSm5yjdu0EsEJH/TSSyxk3QJtA==, tarball: file:projects/devmodel-resources.tgz} + resolution: {integrity: sha512-4ZrlsE0DJ62seKCcvfHeMqCc/JI7gix7sw7G31SBZ5oJbm/4Uz62BplxJ+Gt57wbAzC6X1wTozIPHQI+ksaaZg==, tarball: file:projects/devmodel-resources.tgz} id: file:projects/devmodel-resources.tgz name: '@rush-temp/devmodel-resources' version: 0.0.0 @@ -11942,7 +12138,7 @@ packages: dev: false file:projects/devmodel.tgz_typescript@4.8.4: - resolution: {integrity: sha512-PgNRY82kL7qujBpgY/LHg52igSugdIOfpunDxUE7zq7R9TGU5rAyCdnZvTvfnQ4ZztcC0ICzJ9z0YoqpFjctRQ==, tarball: file:projects/devmodel.tgz} + resolution: {integrity: sha512-eQP4GWVA+cCkwjFfcFy0r+/m/u8acg6ts/YBxVDX36+FmiOuHVvHE7aAciR7uZ0Zhz/SS3rEcW19L7ol9/BVnQ==, tarball: file:projects/devmodel.tgz} id: file:projects/devmodel.tgz name: '@rush-temp/devmodel' version: 0.0.0 @@ -12107,7 +12303,7 @@ packages: dev: false file:projects/generator.tgz: - resolution: {integrity: sha512-9WrSXJKYs18Mv/42PcUJ93qVi9iOCFRVwm6/WMtgeSiZHUwE7EExXtlnMlB7RcpH6H6F8LRLBQ28xTC59duCMg==, tarball: file:projects/generator.tgz} + resolution: {integrity: sha512-MBL1q/ENOolYC7S4zK/F93ToETBseT2NO4sb2uvmw+hVok93zZdGt+L3sX6WZvQSGkIv/GV/Rd5ochzNYvYPcg==, tarball: file:projects/generator.tgz} name: '@rush-temp/generator' version: 0.0.0 dependencies: @@ -12683,7 +12879,7 @@ packages: dev: false file:projects/model-all.tgz_typescript@4.8.4: - resolution: {integrity: sha512-TZENWGRR6McuiodV3kw5TsRolcDNGyj9/oe7KaS98cElBPKtK5+0xmVMsQjCkxkelSS5XU5acxy0F13IVKS+LQ==, tarball: file:projects/model-all.tgz} + resolution: {integrity: sha512-ypjI0QqQeCCDYXOlHjJqJ4Xpq/KaUIvpYqHoCz3kjRHqR3IuW+nOgxCNYfk5oB5kY2MDhWym0KQ5m05WjVfyGA==, tarball: file:projects/model-all.tgz} id: file:projects/model-all.tgz name: '@rush-temp/model-all' version: 0.0.0 @@ -12749,6 +12945,27 @@ packages: - typescript dev: false + file:projects/model-bitrix.tgz_typescript@4.8.4: + resolution: {integrity: sha512-8dZO37adBNzvRYUR8UsawVY1GPRN9F4g4IZj2viQyCZBx0z5vtY+Y+xfoDbHhY23D6pFSBMEz7O0vSCrH2jPSQ==, tarball: file:projects/model-bitrix.tgz} + id: file:projects/model-bitrix.tgz + name: '@rush-temp/model-bitrix' + version: 0.0.0 + dependencies: + '@rushstack/heft': 0.47.11 + '@types/heft-jest': 1.0.3 + '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 + '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + eslint: 8.27.0 + eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566 + eslint-plugin-import: 2.26.0_eslint@8.27.0 + eslint-plugin-n: 15.5.1_eslint@8.27.0 + eslint-plugin-promise: 6.1.1_eslint@8.27.0 + prettier: 2.7.1 + transitivePeerDependencies: + - supports-color + - typescript + dev: false + file:projects/model-board.tgz_typescript@4.8.4: resolution: {integrity: sha512-5I0fthDx1r5yySLmftHGUUPgvFiWEtDZO0M7nLwmfT34wPqhE7EgwsGJHSatDYm6P8e/XCbhl5L55MY58tGrJQ==, tarball: file:projects/model-board.tgz} id: file:projects/model-board.tgz @@ -13869,7 +14086,7 @@ packages: dev: false file:projects/pod-backup.tgz: - resolution: {integrity: sha512-aYuP6b79VLRNZxzds3pAkI3pU7nnflvilszNFThWoIUxrMF6UToUJJZsYWH41PIXprpXJqRIsYGBZhMTQobg0w==, tarball: file:projects/pod-backup.tgz} + resolution: {integrity: sha512-HE0Z8soW6YkoYXsUW6c/sx2ZE9I7T7KHfXtxzNuAbyokYYfaH0vGRUE4y8NZt+bDDAfzzzDOivhu2TPeJUQCYw==, tarball: file:projects/pod-backup.tgz} name: '@rush-temp/pod-backup' version: 0.0.0 dependencies: @@ -14050,7 +14267,7 @@ packages: dev: false file:projects/presentation.tgz_49b4785992daa3b61a639b2b31601e76: - resolution: {integrity: sha512-Nry3q2egydTo0b3dfhd/ejmLBKBROZ6rpyn7943+PJe5vcriJMOazi/OQur0J3LmFl8ozg2IVqeW+A2STDOt+Q==, tarball: file:projects/presentation.tgz} + resolution: {integrity: sha512-s/CjB7oBP/pwiNFtVjC7R1Rs0Ejth0XA+1QgbgYZ3qDqnybD0jbwbXFwnZK8xyCWAK+HSvnzrxI9+SbqGE0p3g==, tarball: file:projects/presentation.tgz} id: file:projects/presentation.tgz name: '@rush-temp/presentation' version: 0.0.0 @@ -14086,7 +14303,7 @@ packages: dev: false file:projects/prod.tgz_b3a81ceaadec606c0eb174aef12a6049: - resolution: {integrity: sha512-xW0K2fEEkHycO18ZB6wSk6u5Qm5X8VLVss47tsS2KHIEwWgLHAIasxhL/SA1DJClWW+kS0o42U2fj76DjLhWJw==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-HF6aMWOyjHsQ6STxaZz0/dFY3zb6o4J+tz/NZYcSTdspxgHb+yuyqvfm+/RPItDiTTDO6GHVogJ8i/WYIkSyhQ==, tarball: file:projects/prod.tgz} id: file:projects/prod.tgz name: '@rush-temp/prod' version: 0.0.0 @@ -14297,7 +14514,7 @@ packages: dev: false file:projects/server-backup.tgz: - resolution: {integrity: sha512-+Sxk2DeLDmhbuG/OMQMwfEJRl/atZE9ZXzdiZotTpd+Et1q9w4WH3h34RWdiCOTiInZyJZXjXF5xDF375VAzpQ==, tarball: file:projects/server-backup.tgz} + resolution: {integrity: sha512-mOaBUJaifcxXZ6AqSpdIiI19QTCADyT/565qkkfja5HI02gLLNdrmyRKoc65YN3UAZbM2C1l+JXMBU8r58St1A==, tarball: file:projects/server-backup.tgz} name: '@rush-temp/server-backup' version: 0.0.0 dependencies: @@ -14923,7 +15140,7 @@ packages: dev: false file:projects/server-tool.tgz: - resolution: {integrity: sha512-sdJKsx2tdPWOmolw3blM7QpXlkN+WUikx/uRmofKYuS/Y1HZkY8UHCYLYzWoDmf2/VplseccWe6HatjkLG6c8Q==, tarball: file:projects/server-tool.tgz} + resolution: {integrity: sha512-5WEO5W7xOR+BMCgmj9G6+cfEtU+FSGcz7Mfb+apZ24IXhwCrn2eVh/YgDGwSRzQV4W3yeIcO0sUbvowKwLhIDQ==, tarball: file:projects/server-tool.tgz} name: '@rush-temp/server-tool' version: 0.0.0 dependencies: @@ -15556,7 +15773,7 @@ packages: dev: false file:projects/tool.tgz: - resolution: {integrity: sha512-otLw170db0pwKQJWhEYqLlU29LqXHyjuS6V/oYcOPxYCvWXe7Nu1NT82TtVD4DgVE4ecgsS1iR1tVfyPwWjeYQ==, tarball: file:projects/tool.tgz} + resolution: {integrity: sha512-eV5BJ5aZRslw3lgvX7imSH6m5EBU8wCOPmZUl4tuKxZAnpjSJA9pHyUivn6EFaJ3r+pdu+ZVSjZ3D/DEbwzAKw==, tarball: file:projects/tool.tgz} name: '@rush-temp/tool' version: 0.0.0 dependencies: @@ -15624,7 +15841,7 @@ packages: dev: false file:projects/tracker-resources.tgz_49b4785992daa3b61a639b2b31601e76: - resolution: {integrity: sha512-IcoVWcO8DdlZyXbAml4GE9UlmPBzihwEzKWq9+Xs8H1U7cDcHhNwGgF+qv8N+gH5mgBHSziEBQrKfnvF+iz5vw==, tarball: file:projects/tracker-resources.tgz} + resolution: {integrity: sha512-z547GvQvHWfDkFJs1VvW4xJKZEzr4O7cICzIfB/lbxZt8uVrbXMVWHxWqfLHrlHxGEalqDeSRdkZ59EQ64cveA==, tarball: file:projects/tracker-resources.tgz} id: file:projects/tracker-resources.tgz name: '@rush-temp/tracker-resources' version: 0.0.0 @@ -15817,7 +16034,7 @@ packages: dev: false file:projects/workbench-resources.tgz_49b4785992daa3b61a639b2b31601e76: - resolution: {integrity: sha512-aZ25sMEnPTO2r84j+DL2Ej0KWk71wnvPdmJSntkdrWZH3HhLYVEd+xY0kYoDB+zm4yxLPvr94hrkoo+IG+LCFA==, tarball: file:projects/workbench-resources.tgz} + resolution: {integrity: sha512-hyxyGITfGafwcakPTdjJ7iQkwz25QiBIWF/QK0/sdPYTqSlxDHxkzvwBh2bKNXGuAPuqKS7aykvhrhHXqZsFXQ==, tarball: file:projects/workbench-resources.tgz} id: file:projects/workbench-resources.tgz name: '@rush-temp/workbench-resources' version: 0.0.0 diff --git a/common/scripts/install-run-rush.js b/common/scripts/install-run-rush.js index ff569fa92a..1639efd2a5 100644 --- a/common/scripts/install-run-rush.js +++ b/common/scripts/install-run-rush.js @@ -40,6 +40,7 @@ const fs = __importStar(require("fs")); const install_run_1 = require("./install-run"); const PACKAGE_NAME = '@microsoft/rush'; const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION'; +const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_RUSH_LOCKFILE_PATH'; function _getRushVersion(logger) { const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION]; if (rushPreviewVersion !== undefined) { @@ -104,7 +105,11 @@ function _run() { (0, install_run_1.runWithErrorAndStatusCode)(logger, () => { const version = _getRushVersion(logger); logger.info(`The rush.json configuration requests Rush version ${version}`); - return (0, install_run_1.installAndRun)(logger, PACKAGE_NAME, version, bin, packageBinArgs); + const lockFilePath = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE]; + if (lockFilePath) { + logger.info(`Found ${INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE}="${lockFilePath}", installing with lockfile.`); + } + return (0, install_run_1.installAndRun)(logger, PACKAGE_NAME, version, bin, packageBinArgs, lockFilePath); }); } _run(); diff --git a/common/scripts/install-run.js b/common/scripts/install-run.js index 208b99c83c..0d281b81ec 100644 --- a/common/scripts/install-run.js +++ b/common/scripts/install-run.js @@ -42,6 +42,7 @@ const os = __importStar(require("os")); const path = __importStar(require("path")); exports.RUSH_JSON_FILENAME = 'rush.json'; const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME = 'RUSH_TEMP_FOLDER'; +const INSTALL_RUN_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_LOCKFILE_PATH'; const INSTALLED_FLAG_FILENAME = 'installed.flag'; const NODE_MODULES_FOLDER_NAME = 'node_modules'; const PACKAGE_JSON_FILENAME = 'package.json'; @@ -82,6 +83,9 @@ function _parsePackageSpecifier(rawPackageSpecifier) { * home directory. * * IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities.copyAndTrimNpmrcFile() + * + * @returns + * The text of the the .npmrc. */ function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) { logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose @@ -121,20 +125,25 @@ function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) { resultLines.push(line); } } - fs.writeFileSync(targetNpmrcPath, resultLines.join(os.EOL)); + const combinedNpmrc = resultLines.join(os.EOL); + fs.writeFileSync(targetNpmrcPath, combinedNpmrc); + return combinedNpmrc; } /** * syncNpmrc() copies the .npmrc file to the target folder, and also trims unusable lines from the .npmrc file. * If the source .npmrc file not exist, then syncNpmrc() will delete an .npmrc that is found in the target folder. * * IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities._syncNpmrc() + * + * @returns + * The text of the the synced .npmrc, if one exists. If one does not exist, then undefined is returned. */ function _syncNpmrc(logger, sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish) { const sourceNpmrcPath = path.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish'); const targetNpmrcPath = path.join(targetNpmrcFolder, '.npmrc'); try { if (fs.existsSync(sourceNpmrcPath)) { - _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath); + return _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath); } else if (fs.existsSync(targetNpmrcPath)) { // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target @@ -306,26 +315,41 @@ function _isPackageAlreadyInstalled(packageInstallFolder) { return false; } } +/** + * Delete a file. Fail silently if it does not exist. + */ +function _deleteFile(file) { + try { + fs.unlinkSync(file); + } + catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; + } + } +} /** * Removes the following files and directories under the specified folder path: * - installed.flag * - * - node_modules */ -function _cleanInstallFolder(rushTempFolder, packageInstallFolder) { +function _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath) { try { const flagFile = path.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME); - if (fs.existsSync(flagFile)) { - fs.unlinkSync(flagFile); - } + _deleteFile(flagFile); const packageLockFile = path.resolve(packageInstallFolder, 'package-lock.json'); - if (fs.existsSync(packageLockFile)) { - fs.unlinkSync(packageLockFile); + if (lockFilePath) { + fs.copyFileSync(lockFilePath, packageLockFile); } - const nodeModulesFolder = path.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME); - if (fs.existsSync(nodeModulesFolder)) { - const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler'); - fs.renameSync(nodeModulesFolder, path.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`)); + else { + // Not running `npm ci`, so need to cleanup + _deleteFile(packageLockFile); + const nodeModulesFolder = path.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME); + if (fs.existsSync(nodeModulesFolder)) { + const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler'); + fs.renameSync(nodeModulesFolder, path.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`)); + } } } catch (e) { @@ -354,17 +378,17 @@ function _createPackageJson(packageInstallFolder, name, version) { /** * Run "npm install" in the package install folder. */ -function _installPackage(logger, packageInstallFolder, name, version) { +function _installPackage(logger, packageInstallFolder, name, version, command) { try { logger.info(`Installing ${name}...`); const npmPath = getNpmPath(); - const result = childProcess.spawnSync(npmPath, ['install'], { + const result = childProcess.spawnSync(npmPath, [command], { stdio: 'inherit', cwd: packageInstallFolder, env: process.env }); if (result.status !== 0) { - throw new Error('"npm install" encountered an error'); + throw new Error(`"npm ${command}" encountered an error`); } logger.info(`Successfully installed ${name}@${version}`); } @@ -392,18 +416,19 @@ function _writeFlagFile(packageInstallFolder) { throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`); } } -function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs) { +function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs, lockFilePath = process.env[INSTALL_RUN_LOCKFILE_PATH_VARIABLE]) { const rushJsonFolder = findRushJsonFolder(); const rushCommonFolder = path.join(rushJsonFolder, 'common'); const rushTempFolder = _getRushTempFolder(rushCommonFolder); const packageInstallFolder = _ensureAndJoinPath(rushTempFolder, 'install-run', `${packageName}@${packageVersion}`); if (!_isPackageAlreadyInstalled(packageInstallFolder)) { // The package isn't already installed - _cleanInstallFolder(rushTempFolder, packageInstallFolder); + _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath); const sourceNpmrcFolder = path.join(rushCommonFolder, 'config', 'rush'); _syncNpmrc(logger, sourceNpmrcFolder, packageInstallFolder); _createPackageJson(packageInstallFolder, packageName, packageVersion); - _installPackage(logger, packageInstallFolder, packageName, packageVersion); + const command = lockFilePath ? 'ci' : 'install'; + _installPackage(logger, packageInstallFolder, packageName, packageVersion, command); _writeFlagFile(packageInstallFolder); } const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`; diff --git a/dev/prod/package.json b/dev/prod/package.json index b25470f3c4..cb1b59ac2b 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -154,6 +154,10 @@ "@hcengineering/server-hr-resources": "~0.6.0", "@hcengineering/document": "^0.6.0", "@hcengineering/document-assets": "^0.6.0", - "@hcengineering/document-resources": "^0.6.0" + "@hcengineering/document-resources": "^0.6.0", + + "@hcengineering/bitrix": "^0.6.0", + "@hcengineering/bitrix-assets": "^0.6.0", + "@hcengineering/bitrix-resources": "^0.6.0" } } diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 338bdfa4f0..a0e6e77a64 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -44,6 +44,8 @@ import { hrId } from '@hcengineering/hr' import rekoni from '@hcengineering/rekoni' import document, { documentId } from '@hcengineering/document' +import bitrix, { bitrixId } from '@hcengineering/bitrix' + import '@hcengineering/login-assets' import '@hcengineering/task-assets' import '@hcengineering/view-assets' @@ -68,6 +70,7 @@ import '@hcengineering/board-assets' import '@hcengineering/preference-assets' import '@hcengineering/hr-assets' import '@hcengineering/document-assets' +import '@hcengineering/bitrix-assets' import presentation, { presentationId } from '@hcengineering/presentation' import { coreId } from '@hcengineering/core' @@ -134,6 +137,7 @@ export async function configurePlatform() { addLocation(automationId, () => import(/* webpackChunkName: "automation" */ '@hcengineering/automation-resources')) addLocation(hrId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/hr-resources')) addLocation(documentId, () => import(/* webpackChunkName: "hr" */ '@hcengineering/document-resources')) + addLocation(bitrixId, () => import(/* webpackChunkName: "bitrix" */ '@hcengineering/bitrix-resources')) setMetadata(workbench.metadata.PlatformTitle, 'Platform') } diff --git a/models/all/package.json b/models/all/package.json index 8c86785eed..18e01fe9d7 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -74,6 +74,7 @@ "@hcengineering/model-preference": "~0.6.0", "@hcengineering/model-hr": "~0.6.0", "@hcengineering/model-server-hr": "~0.6.0", - "@hcengineering/model-document": "~0.6.0" + "@hcengineering/model-document": "~0.6.0", + "@hcengineering/model-bitrix": "~0.6.0" } } diff --git a/models/all/src/index.ts b/models/all/src/index.ts index 720c3b30a5..2f7f5d0620 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -59,6 +59,7 @@ import { createModel as preferenceModel } from '@hcengineering/model-preference' import { createModel as hrModel } from '@hcengineering/model-hr' import { createModel as serverHrModel } from '@hcengineering/model-server-hr' import { createModel as documentModel } from '@hcengineering/model-document' +import { createModel as bitrixModel } from '@hcengineering/model-bitrix' export const version: Data = jsonVersion as Data @@ -90,6 +91,7 @@ const builders: [(b: Builder) => void, string][] = [ [trackerModel, 'tracker'], [boardModel, 'board'], [calendarModel, 'calendar'], + [bitrixModel, 'bitrix'], [serverCoreModel, 'server-core'], [serverAttachmentModel, 'server-attachment'], diff --git a/models/all/src/migration.ts b/models/all/src/migration.ts index 727ad48eb4..1507bca477 100644 --- a/models/all/src/migration.ts +++ b/models/all/src/migration.ts @@ -35,6 +35,7 @@ import { boardOperation } from '@hcengineering/model-board' import { demoOperation } from '@hcengineering/model-demo' import { hrOperation } from '@hcengineering/model-hr' import { documentOperation } from '@hcengineering/model-document' +import { bitrixOperation } from '@hcengineering/model-bitrix' export const migrateOperations: MigrateOperation[] = [ coreOperation, @@ -56,5 +57,6 @@ export const migrateOperations: MigrateOperation[] = [ trackerOperation, boardOperation, hrOperation, - documentOperation + documentOperation, + bitrixOperation ] diff --git a/models/bitrix/.eslintrc.js b/models/bitrix/.eslintrc.js new file mode 100644 index 0000000000..59eb1482fa --- /dev/null +++ b/models/bitrix/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/model-rig/profiles/default/config/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/bitrix/.npmignore b/models/bitrix/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/bitrix/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/bitrix/config/rig.json b/models/bitrix/config/rig.json new file mode 100644 index 0000000000..45672e5d25 --- /dev/null +++ b/models/bitrix/config/rig.json @@ -0,0 +1,18 @@ +// The "rig.json" file directs tools to look for their config files in an external package. +// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + /** + * (Required) The name of the rig package to inherit from. + * It should be an NPM package name with the "-rig" suffix. + */ + "rigPackageName": "@hcengineering/model-rig" + + /** + * (Optional) Selects a config profile from the rig package. The name must consist of + * lowercase alphanumeric words separated by hyphens, for example "sample-profile". + * If omitted, then the "default" profile will be used." + */ + // "rigProfile": "your-profile-name" +} diff --git a/models/bitrix/package.json b/models/bitrix/package.json new file mode 100644 index 0000000000..49ac89d991 --- /dev/null +++ b/models/bitrix/package.json @@ -0,0 +1,45 @@ +{ + "name": "@hcengineering/model-bitrix", + "version": "0.6.0", + "main": "lib/index.js", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "heft build", + "build:watch": "tsc", + "lint:fix": "eslint --fix src", + "lint": "eslint src", + "format": "prettier --write src && eslint --fix src" + }, + "devDependencies": { + "@hcengineering/model-rig": "~0.6.0", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.26.0", + "@types/heft-jest": "^1.0.3", + "@typescript-eslint/parser": "^5.41.0", + "eslint-config-standard-with-typescript": "^23.0.0", + "prettier": "^2.7.1", + "@rushstack/heft": "^0.47.9" + }, + "dependencies": { + "@hcengineering/activity": "~0.6.0", + "@hcengineering/model": "~0.6.0", + "@hcengineering/core": "^0.6.19", + "@hcengineering/platform": "^0.6.8", + "@hcengineering/model-core": "~0.6.0", + "@hcengineering/model-attachment": "~0.6.0", + "@hcengineering/model-contact": "~0.6.1", + "@hcengineering/view": "^0.6.1", + "@hcengineering/model-view": "~0.6.0", + "@hcengineering/contact": "~0.6.8", + "@hcengineering/bitrix": "^0.6.0", + "@hcengineering/bitrix-resources": "^0.6.0", + "@hcengineering/preference": "^0.6.1", + "@hcengineering/model-preference": "~0.6.0", + "@hcengineering/setting": "~0.6.1", + "@hcengineering/ui": "^0.6.2" + } +} diff --git a/models/bitrix/src/index.ts b/models/bitrix/src/index.ts new file mode 100644 index 0000000000..9a29c0c6fb --- /dev/null +++ b/models/bitrix/src/index.ts @@ -0,0 +1,99 @@ +// +// Copyright © 2020, 2021 Anticrm Platform Contributors. +// Copyright © 2021 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Builder, Collection, Mixin, Model, Prop, TypeRef, TypeString } from '@hcengineering/model' +import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' +import bitrix from './plugin' + +import { BitrixEntityMapping, BitrixFieldMapping, BitrixSyncDoc, Fields } from '@hcengineering/bitrix' +import { AnyAttribute, Class, Doc, Domain, Ref } from '@hcengineering/core' + +import view, { createAction } from '@hcengineering/model-view' + +import setting from '@hcengineering/setting' + +const DOMAIN_BITRIX = 'bitrix' as Domain + +@Mixin(bitrix.mixin.BitrixSyncDoc, core.class.Doc) +export class TBitrixSyncDoc extends TDoc implements BitrixSyncDoc { + type!: string + bitrixId!: string +} + +@Model(bitrix.class.EntityMapping, core.class.Doc, DOMAIN_BITRIX) +export class TBitrixEntityMapping extends TDoc implements BitrixEntityMapping { + @Prop(TypeRef(core.class.Attribute), core.string.ClassPropertyLabel) + ofClass!: Ref> + + type!: string + + @Prop(Collection(bitrix.class.FieldMapping), bitrix.string.FieldMapping) + fields!: number + + bitrixFields!: Fields + comments!: boolean + activity!: boolean + attachments!: boolean +} + +@Model(bitrix.class.FieldMapping, core.class.Doc, DOMAIN_BITRIX) +export class TBitrixFieldMapping extends TAttachedDoc implements BitrixFieldMapping { + @Prop(TypeRef(core.class.Attribute), core.string.ClassPropertyLabel) + ofClass!: Ref> + + @Prop(TypeString(), core.string.ClassPropertyLabel) + attributeName!: Ref + + bitrixTitle!: string + bitrixType!: string + operation!: any +} + +export function createModel (builder: Builder): void { + builder.createModel(TBitrixEntityMapping, TBitrixFieldMapping, TBitrixSyncDoc) + + builder.createDoc( + setting.class.IntegrationType, + core.space.Model, + { + label: bitrix.string.Bitrix, + description: bitrix.string.BitrixDesc, + icon: bitrix.component.BitrixIcon, + createComponent: bitrix.component.BitrixConnect, + configureComponent: bitrix.component.BitrixConfigure + }, + bitrix.integrationType.Bitrix + ) + + createAction( + builder, + { + action: view.actionImpl.ShowPopup, + actionProps: { + component: bitrix.component.BitrixImport + }, + label: bitrix.string.BitrixImport, + icon: bitrix.icon.Bitrix, + input: 'any', + category: view.category.General, + target: core.class.Doc, + context: { mode: ['workbench', 'browser', 'editor', 'panel', 'popup'], group: 'create' } + }, + bitrix.action.BitrixImport + ) +} + +export { bitrixOperation } from './migration' diff --git a/models/bitrix/src/migration.ts b/models/bitrix/src/migration.ts new file mode 100644 index 0000000000..3e20bd1b32 --- /dev/null +++ b/models/bitrix/src/migration.ts @@ -0,0 +1,21 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model' + +export const bitrixOperation: MigrateOperation = { + async migrate (client: MigrationClient): Promise {}, + async upgrade (client: MigrationUpgradeClient): Promise {} +} diff --git a/models/bitrix/src/plugin.ts b/models/bitrix/src/plugin.ts new file mode 100644 index 0000000000..7e215d21cd --- /dev/null +++ b/models/bitrix/src/plugin.ts @@ -0,0 +1,37 @@ +// +// Copyright © 2020, 2021 Anticrm Platform Contributors. +// Copyright © 2021 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { bitrixId } from '@hcengineering/bitrix' +import bitrix from '@hcengineering/bitrix-resources/src/plugin' +import { IntlString, mergeIds } from '@hcengineering/platform' +import { Action } from '@hcengineering/view' + +import { Ref } from '@hcengineering/core' +import type { AnyComponent } from '@hcengineering/ui' + +export default mergeIds(bitrixId, bitrix, { + component: { + BitrixConnect: '' as AnyComponent, + BitrixConfigure: '' as AnyComponent, + BitrixImport: '' as AnyComponent + }, + string: { + BitrixImport: '' as IntlString + }, + action: { + BitrixImport: '' as Ref + } +}) diff --git a/models/bitrix/tsconfig.json b/models/bitrix/tsconfig.json new file mode 100644 index 0000000000..807a92c38c --- /dev/null +++ b/models/bitrix/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./node_modules/@hcengineering/model-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + } +} \ No newline at end of file diff --git a/packages/core/src/operations.ts b/packages/core/src/operations.ts index 295e3e4ddb..d6212fe3d7 100644 --- a/packages/core/src/operations.ts +++ b/packages/core/src/operations.ts @@ -1,5 +1,5 @@ import { DocumentUpdate, Hierarchy, MixinData, MixinUpdate, ModelDb } from '.' -import type { Account, AttachedData, AttachedDoc, Class, Data, Doc, Mixin, Ref, Space } from './classes' +import type { Account, AttachedData, AttachedDoc, Class, Data, Doc, Mixin, Ref, Space, Timestamp } from './classes' import { Client } from './client' import core from './component' import type { DocumentQuery, FindOptions, FindResult, TxResult, WithLookup } from './storage' @@ -55,9 +55,11 @@ export class TxOperations implements Omit { _class: Ref>, space: Ref, attributes: Data, - id?: Ref + id?: Ref, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise> { - const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id) + const tx = this.txFactory.createTxCreateDoc(_class, space, attributes, id, modifiedOn, modifiedBy) await this.client.tx(tx) return tx.objectId } @@ -69,14 +71,16 @@ export class TxOperations implements Omit { attachedToClass: Ref>, collection: string, attributes: AttachedData

, - id?: Ref

+ id?: Ref

, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise> { const tx = this.txFactory.createTxCollectionCUD( attachedToClass, attachedTo, space, collection, - this.txFactory.createTxCreateDoc

(_class, space, attributes as unknown as Data

, id) + this.txFactory.createTxCreateDoc

(_class, space, attributes as unknown as Data

, id, modifiedOn, modifiedBy) ) await this.client.tx(tx) return tx.tx.objectId as unknown as Ref

@@ -90,14 +94,16 @@ export class TxOperations implements Omit { attachedToClass: Ref>, collection: string, operations: DocumentUpdate

, - retrieve?: boolean + retrieve?: boolean, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise> { const tx = this.txFactory.createTxCollectionCUD( attachedToClass, attachedTo, space, collection, - this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve) + this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy) ) await this.client.tx(tx) return tx.objectId @@ -109,14 +115,16 @@ export class TxOperations implements Omit { objectId: Ref

, attachedTo: Ref, attachedToClass: Ref>, - collection: string + collection: string, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise> { const tx = this.txFactory.createTxCollectionCUD( attachedToClass, attachedTo, space, collection, - this.txFactory.createTxRemoveDoc(_class, space, objectId) + this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy) ) await this.client.tx(tx) return tx.objectId @@ -127,14 +135,22 @@ export class TxOperations implements Omit { space: Ref, objectId: Ref, operations: DocumentUpdate, - retrieve?: boolean + retrieve?: boolean, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise { - const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve) + const tx = this.txFactory.createTxUpdateDoc(_class, space, objectId, operations, retrieve, modifiedOn, modifiedBy) return this.client.tx(tx) } - removeDoc(_class: Ref>, space: Ref, objectId: Ref): Promise { - const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId) + removeDoc( + _class: Ref>, + space: Ref, + objectId: Ref, + modifiedOn?: Timestamp, + modifiedBy?: Ref + ): Promise { + const tx = this.txFactory.createTxRemoveDoc(_class, space, objectId, modifiedOn, modifiedBy) return this.client.tx(tx) } @@ -143,9 +159,19 @@ export class TxOperations implements Omit { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinData + attributes: MixinData, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise { - const tx = this.txFactory.createTxMixin(objectId, objectClass, objectSpace, mixin, attributes) + const tx = this.txFactory.createTxMixin( + objectId, + objectClass, + objectSpace, + mixin, + attributes, + modifiedOn, + modifiedBy + ) return this.client.tx(tx) } @@ -154,17 +180,33 @@ export class TxOperations implements Omit { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinUpdate + attributes: MixinUpdate, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): Promise { - const tx = this.txFactory.createTxMixin(objectId, objectClass, objectSpace, mixin, attributes) + const tx = this.txFactory.createTxMixin( + objectId, + objectClass, + objectSpace, + mixin, + attributes, + modifiedOn, + modifiedBy + ) return this.client.tx(tx) } - update(doc: T, update: DocumentUpdate, retrieve?: boolean): Promise { + update( + doc: T, + update: DocumentUpdate, + retrieve?: boolean, + modifiedOn?: Timestamp, + modifiedBy?: Ref + ): Promise { const hierarchy = this.client.getHierarchy() if (hierarchy.isMixin(doc._class)) { const baseClass = hierarchy.getBaseClass(doc._class) - return this.updateMixin(doc._id, baseClass, doc.space, doc._class, update) + return this.updateMixin(doc._id, baseClass, doc.space, doc._class, update, modifiedOn, modifiedBy) } if (hierarchy.isDerived(doc._class, core.class.AttachedDoc)) { const adoc = doc as unknown as AttachedDoc @@ -176,13 +218,15 @@ export class TxOperations implements Omit { adoc.attachedToClass, adoc.collection, update, - retrieve + retrieve, + modifiedOn, + modifiedBy ) } - return this.updateDoc(doc._class, doc.space, doc._id, update, retrieve) + return this.updateDoc(doc._class, doc.space, doc._id, update, retrieve, modifiedOn, modifiedBy) } - remove(doc: T): Promise { + remove(doc: T, modifiedOn?: Timestamp, modifiedBy?: Ref): Promise { if (this.client.getHierarchy().isDerived(doc._class, core.class.AttachedDoc)) { const adoc = doc as unknown as AttachedDoc return this.removeCollection( @@ -191,7 +235,9 @@ export class TxOperations implements Omit { adoc._id, adoc.attachedTo, adoc.attachedToClass, - adoc.collection + adoc.collection, + modifiedOn, + modifiedBy ) } return this.removeDoc(doc._class, doc.space, doc._id) diff --git a/packages/core/src/tx.ts b/packages/core/src/tx.ts index ad107fb182..3da4d12e0f 100644 --- a/packages/core/src/tx.ts +++ b/packages/core/src/tx.ts @@ -14,7 +14,20 @@ // import type { KeysByType } from 'simplytyped' -import type { Account, Arr, AttachedDoc, Class, Data, Doc, Domain, Mixin, PropertyType, Ref, Space } from './classes' +import type { + Account, + Arr, + AttachedDoc, + Class, + Data, + Doc, + Domain, + Mixin, + PropertyType, + Ref, + Space, + Timestamp +} from './classes' import core from './component' import { setObjectValue } from './objvalue' import { _getOperator } from './operator' @@ -398,7 +411,9 @@ export class TxFactory { _class: Ref>, space: Ref, attributes: Data, - objectId?: Ref + objectId?: Ref, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): TxCreateDoc { return { _id: generateId(), @@ -407,8 +422,8 @@ export class TxFactory { objectId: objectId ?? generateId(), objectClass: _class, objectSpace: space, - modifiedOn: Date.now(), - modifiedBy: this.account, + modifiedOn: modifiedOn ?? Date.now(), + modifiedBy: modifiedBy ?? this.account, attributes } } @@ -418,7 +433,9 @@ export class TxFactory { objectId: Ref, space: Ref, collection: string, - tx: TxCUD

+ tx: TxCUD

, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): TxCollectionCUD { return { _id: generateId(), @@ -427,8 +444,8 @@ export class TxFactory { objectId, objectClass: _class, objectSpace: space, - modifiedOn: Date.now(), - modifiedBy: this.account, + modifiedOn: modifiedOn ?? Date.now(), + modifiedBy: modifiedBy ?? this.account, collection, tx } @@ -439,14 +456,16 @@ export class TxFactory { space: Ref, objectId: Ref, operations: DocumentUpdate, - retrieve?: boolean + retrieve?: boolean, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): TxUpdateDoc { return { _id: generateId(), _class: core.class.TxUpdateDoc, space: core.space.Tx, - modifiedBy: this.account, - modifiedOn: Date.now(), + modifiedBy: modifiedBy ?? this.account, + modifiedOn: modifiedOn ?? Date.now(), objectId, objectClass: _class, objectSpace: space, @@ -455,13 +474,19 @@ export class TxFactory { } } - createTxRemoveDoc(_class: Ref>, space: Ref, objectId: Ref): TxRemoveDoc { + createTxRemoveDoc( + _class: Ref>, + space: Ref, + objectId: Ref, + modifiedOn?: Timestamp, + modifiedBy?: Ref + ): TxRemoveDoc { return { _id: generateId(), _class: core.class.TxRemoveDoc, space: core.space.Tx, - modifiedBy: this.account, - modifiedOn: Date.now(), + modifiedBy: modifiedBy ?? this.account, + modifiedOn: modifiedOn ?? Date.now(), objectId, objectClass: _class, objectSpace: space @@ -473,14 +498,16 @@ export class TxFactory { objectClass: Ref>, objectSpace: Ref, mixin: Ref>, - attributes: MixinUpdate + attributes: MixinUpdate, + modifiedOn?: Timestamp, + modifiedBy?: Ref ): TxMixin { return { _id: generateId(), _class: core.class.TxMixin, space: core.space.Tx, - modifiedBy: this.account, - modifiedOn: Date.now(), + modifiedBy: modifiedBy ?? this.account, + modifiedOn: modifiedOn ?? Date.now(), objectId, objectClass, objectSpace, @@ -489,13 +516,20 @@ export class TxFactory { } } - createTxApplyIf (space: Ref, scope: string, match: DocumentClassQuery[], txes: TxCUD[]): TxApplyIf { + createTxApplyIf ( + space: Ref, + scope: string, + match: DocumentClassQuery[], + txes: TxCUD[], + modifiedOn?: Timestamp, + modifiedBy?: Ref + ): TxApplyIf { return { _id: generateId(), _class: core.class.TxApplyIf, space: core.space.Tx, - modifiedBy: this.account, - modifiedOn: Date.now(), + modifiedBy: modifiedBy ?? this.account, + modifiedOn: modifiedOn ?? Date.now(), objectSpace: space, scope, match, diff --git a/packages/presentation/src/components/Card.svelte b/packages/presentation/src/components/Card.svelte index 0e991ab787..a99b46291d 100644 --- a/packages/presentation/src/components/Card.svelte +++ b/packages/presentation/src/components/Card.svelte @@ -27,6 +27,7 @@ export let createMore: boolean | undefined = undefined export let okLabel: IntlString = presentation.string.Create export let onCancel: Function | undefined = undefined + export let fullSize = false const dispatch = createEventDispatcher() @@ -36,6 +37,7 @@

{}} use:resizeObserver={() => { dispatch('changeContent') diff --git a/packages/presentation/src/components/ObjectPopup.svelte b/packages/presentation/src/components/ObjectPopup.svelte index 889bc942d2..d3463e25b0 100644 --- a/packages/presentation/src/components/ObjectPopup.svelte +++ b/packages/presentation/src/components/ObjectPopup.svelte @@ -64,12 +64,14 @@ const dispatch = createEventDispatcher() const query = createQuery() + + $: _idExtra = typeof docQuery?._id === 'object' ? docQuery?._id : {} $: query.query( _class, { ...(docQuery ?? {}), [searchField]: { $like: '%' + search + '%' }, - _id: { $nin: ignoreObjects } + _id: { $nin: ignoreObjects, ..._idExtra } }, (result) => { result.sort((a, b) => { diff --git a/packages/theme/styles/components.scss b/packages/theme/styles/components.scss index 8277136770..b76f91dd47 100644 --- a/packages/theme/styles/components.scss +++ b/packages/theme/styles/components.scss @@ -214,13 +214,19 @@ margin: 2.5rem; } } + &.vScroll { + overflow-y: auto; + &::-webkit-scrollbar-track { + margin: 2.5rem; + } + } .ac-column { display: flex; flex-direction: column; overflow-y: auto; - padding: 1.75rem 2.5rem; - min-width: 25rem; + padding: 0.75rem 0.5rem; + min-width: 15rem; max-width: 25rem; height: 100%; border-right: 1px solid var(--theme-menu-divider); diff --git a/packages/theme/styles/dialogs.scss b/packages/theme/styles/dialogs.scss index c80fcadf81..119d21da8c 100644 --- a/packages/theme/styles/dialogs.scss +++ b/packages/theme/styles/dialogs.scss @@ -139,6 +139,10 @@ height: auto; max-width: 60rem; max-height: inherit; + &.full { + width: max-content; + // max-width: 100%; + } } &.mobile { width: 90vw; diff --git a/packages/ui/src/components/DropdownLabels.svelte b/packages/ui/src/components/DropdownLabels.svelte index b8ffb1a125..78e7d92e01 100644 --- a/packages/ui/src/components/DropdownLabels.svelte +++ b/packages/ui/src/components/DropdownLabels.svelte @@ -37,6 +37,7 @@ export let focusIndex = -1 export let autoSelect: boolean = true export let useFlexGrow = false + export let minW0 = true let container: HTMLElement let opened: boolean = false @@ -53,7 +54,7 @@ const mgr = getFocusManager() -
+
diff --git a/plugins/bitrix-resources/src/components/BitrixConfigure.svelte b/plugins/bitrix-resources/src/components/BitrixConfigure.svelte new file mode 100644 index 0000000000..425fa39934 --- /dev/null +++ b/plugins/bitrix-resources/src/components/BitrixConfigure.svelte @@ -0,0 +1,96 @@ + + + + { + dispatch('close') + }} + canSave={true} + okLabel={presentation.string.Ok} + on:close={() => dispatch('close')} + fullSize={true} +> + + {#if profile} +
+ {profile.LAST_NAME} + {profile.NAME} +
+ {/if} +
+ {#if profile} + {#if !profile.ADMIN} +
diff --git a/plugins/bitrix-resources/src/components/BitrixConnect.svelte b/plugins/bitrix-resources/src/components/BitrixConnect.svelte new file mode 100644 index 0000000000..878f772107 --- /dev/null +++ b/plugins/bitrix-resources/src/components/BitrixConnect.svelte @@ -0,0 +1,44 @@ + + + + dispatch('close')} +> + + + diff --git a/plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte b/plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte new file mode 100644 index 0000000000..9b4e7c2c59 --- /dev/null +++ b/plugins/bitrix-resources/src/components/BitrixFieldLookup.svelte @@ -0,0 +1,142 @@ + + +
+
+
+ +
+
+ + {#each Object.entries(fields) as kv} + {@const field = kv[1]} + {@const title = field.formLabel ?? field.title} + {#if title.includes(pattern) || field.type.includes(pattern) || pattern.length === 0} + + {title} : {field.type} + + "{kv[0]}" + + {#if field.type === 'enumeration' || field.type === 'crm_status'} +
diff --git a/plugins/bitrix-resources/src/components/BitrixImport.svelte b/plugins/bitrix-resources/src/components/BitrixImport.svelte new file mode 100644 index 0000000000..0a6ba8b182 --- /dev/null +++ b/plugins/bitrix-resources/src/components/BitrixImport.svelte @@ -0,0 +1,61 @@ + + + { + dispatch('close') + }} + okLabel={getEmbeddedLabel('Close')} + on:close +> + {#if integration && bitrixClient} + {#each mappings as mapping} + + {/each} + {:else} + diff --git a/plugins/bitrix-resources/src/components/CreateMapping.svelte b/plugins/bitrix-resources/src/components/CreateMapping.svelte new file mode 100644 index 0000000000..0ef7192edb --- /dev/null +++ b/plugins/bitrix-resources/src/components/CreateMapping.svelte @@ -0,0 +1,67 @@ + + + +
+ + +
+
diff --git a/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte b/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte new file mode 100644 index 0000000000..7d877c7a8f --- /dev/null +++ b/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte @@ -0,0 +1,43 @@ + + + + + + {#if _kind === MappingOperation.CopyValue} + + {:else if _kind === MappingOperation.CreateTag} + + {:else if _kind === MappingOperation.CreateChannel} + + {/if} + diff --git a/plugins/bitrix-resources/src/components/EntityMapping.svelte b/plugins/bitrix-resources/src/components/EntityMapping.svelte new file mode 100644 index 0000000000..77257794d7 --- /dev/null +++ b/plugins/bitrix-resources/src/components/EntityMapping.svelte @@ -0,0 +1,167 @@ + + + +
+ + +
+
+
+ +
+
+ client.update(mapping, { comments: evt.detail })} + /> +
+
+
+
+ client.update(mapping, { attachments: evt.detail })} + /> +
+
+
+
+ client.update(mapping, { activity: evt.detail })} + /> +
+
+
+
+
+ +
+ {#each Object.entries(fieldsByClass) as field, i} + {@const cl = client.getHierarchy().getClass(toClassRef(field[0]))} +
+ {#if cl.icon} +
+ +
+ {/if} +
+
+ {#each field[1] as cfield, i} +
+ {i + 1}. + +
+ {/each} +
+ {/each} +
+
+ + + + + + +
+
diff --git a/plugins/bitrix-resources/src/components/EnumPopup.svelte b/plugins/bitrix-resources/src/components/EnumPopup.svelte new file mode 100644 index 0000000000..bbc28ae2d1 --- /dev/null +++ b/plugins/bitrix-resources/src/components/EnumPopup.svelte @@ -0,0 +1,46 @@ + + + + { + if (evt.detail != null) { + action(evt.detail) + } + dispatch('close') + }} +> + + {item.name} + + diff --git a/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte new file mode 100644 index 0000000000..e07bb4bf51 --- /dev/null +++ b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte @@ -0,0 +1,34 @@ + + +
+ {#if attr} +
+
+ {/if} + + {#if mapping && mapping.bitrixFields} + {#if kind === MappingOperation.CopyValue} + + {:else if kind === MappingOperation.CreateTag} + + {:else if kind === MappingOperation.CreateChannel} + + {/if} + {/if} +
diff --git a/plugins/bitrix-resources/src/components/FieldMappingSynchronizer.svelte b/plugins/bitrix-resources/src/components/FieldMappingSynchronizer.svelte new file mode 100644 index 0000000000..79763587b2 --- /dev/null +++ b/plugins/bitrix-resources/src/components/FieldMappingSynchronizer.svelte @@ -0,0 +1,389 @@ + + + + +
+ { + space = evt.detail + }} + autoSelect + spaceQuery={{ _id: { $in: [contact.space.Contacts] } }} + /> + +
+ { + if (val) { + limit = val + } + }} + /> +
+
+
+ {state} +
+
+
+
+
+ {#each Object.entries(fieldsByClass) as field, i} + {@const cl = client.getHierarchy().getClass(toClassRef(field[0]))} +
+ {#if cl.icon} +
+ +
+ {/if} +
+
+ {#each field[1] as cfield, i} +
+ {i + 1}. + +
+ {/each} +
+ {/each} +
+
diff --git a/plugins/bitrix-resources/src/components/icons/Bitrix.svelte b/plugins/bitrix-resources/src/components/icons/Bitrix.svelte new file mode 100644 index 0000000000..407f7e6e9a --- /dev/null +++ b/plugins/bitrix-resources/src/components/icons/Bitrix.svelte @@ -0,0 +1,38 @@ + + + +
+ + + + + + +
diff --git a/plugins/bitrix-resources/src/components/mappings/CopyMapping.svelte b/plugins/bitrix-resources/src/components/mappings/CopyMapping.svelte new file mode 100644 index 0000000000..c5a2234333 --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CopyMapping.svelte @@ -0,0 +1,134 @@ + + +
+ {#each patterns as p, i} +
+ {#if attribute.type._class !== core.class.EnumOf} + + {/if} + + {#if p.alternatives} + {#each p.alternatives as alt, i} + +
+
+ {/each} + {#if attribute.type._class !== core.class.EnumOf || patterns.length === 0} +
+
+ {/if} +
+ + diff --git a/plugins/bitrix-resources/src/components/mappings/CopyMappingPresenter.svelte b/plugins/bitrix-resources/src/components/mappings/CopyMappingPresenter.svelte new file mode 100644 index 0000000000..e52a9c72d0 --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CopyMappingPresenter.svelte @@ -0,0 +1,48 @@ + + +
+ {#each op.patterns as p, i} +
+ + {p.text} + + {#if mapping.bitrixFields} + => {fieldOf(p.field)} + {/if} + {#if p.alternatives} + {#each p.alternatives as alt, i} + |{fieldOf(alt)} + {/each} + {/if} +
+ {/each} +
+ + diff --git a/plugins/bitrix-resources/src/components/mappings/CreateChannelMapping.svelte b/plugins/bitrix-resources/src/components/mappings/CreateChannelMapping.svelte new file mode 100644 index 0000000000..9b4c49f937 --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CreateChannelMapping.svelte @@ -0,0 +1,116 @@ + + +
+ {#each channelFields as p, i} +
+ + + +
+
+
+ {/each} +
+
+
+ + diff --git a/plugins/bitrix-resources/src/components/mappings/CreateChannelMappingPresenter.svelte b/plugins/bitrix-resources/src/components/mappings/CreateChannelMappingPresenter.svelte new file mode 100644 index 0000000000..60db71f2ab --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CreateChannelMappingPresenter.svelte @@ -0,0 +1,46 @@ + + +
+ {#each op.fields as p, i} +
+ + -> + {#if mapping.bitrixFields} + {p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''} + {/if} +
+ {/each} +
+ + diff --git a/plugins/bitrix-resources/src/components/mappings/CreateTagMapping.svelte b/plugins/bitrix-resources/src/components/mappings/CreateTagMapping.svelte new file mode 100644 index 0000000000..c38321918c --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CreateTagMapping.svelte @@ -0,0 +1,137 @@ + + +
+ {#each tagFields as p, i} + {@const tagIcon = tagLevel[p.weight % 3]} + {@const tagLabel = labels[Math.floor(p.weight / 3)]} +
+ + +
+
+ {/each} +
+
+ + + diff --git a/plugins/bitrix-resources/src/components/mappings/CreateTagMappingPresenter.svelte b/plugins/bitrix-resources/src/components/mappings/CreateTagMappingPresenter.svelte new file mode 100644 index 0000000000..b04d3f12df --- /dev/null +++ b/plugins/bitrix-resources/src/components/mappings/CreateTagMappingPresenter.svelte @@ -0,0 +1,49 @@ + + +
+ {#each op.fields as p, i} + {@const tagIcon = tagLevel[p.weight % 3]} + {@const tagLabel = labels[Math.floor(p.weight / 3)]} + +
+ {#if mapping.bitrixFields} + {p.field ? mapping.bitrixFields[p.field]?.formLabel ?? mapping.bitrixFields[p.field]?.title : p.field ?? ''} + {/if} + +
+ {/each} +
+ + diff --git a/plugins/bitrix-resources/src/index.ts b/plugins/bitrix-resources/src/index.ts new file mode 100644 index 0000000000..0ca19335ff --- /dev/null +++ b/plugins/bitrix-resources/src/index.ts @@ -0,0 +1,33 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Resources } from '@hcengineering/platform' + +import BitrixConnect from './components/BitrixConnect.svelte' +import BitrixConfigure from './components/BitrixConfigure.svelte' +import BitrixIcon from './components/icons/Bitrix.svelte' +import BitrixImport from './components/BitrixImport.svelte' + +export default async (): Promise => ({ + component: { + BitrixIcon, + BitrixConnect, + BitrixConfigure, + BitrixImport + }, + handler: { + DisconnectHandler: async () => {} + } +}) diff --git a/plugins/bitrix-resources/src/plugin.ts b/plugins/bitrix-resources/src/plugin.ts new file mode 100644 index 0000000000..c052f98e2e --- /dev/null +++ b/plugins/bitrix-resources/src/plugin.ts @@ -0,0 +1,47 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { IntlString, mergeIds } from '@hcengineering/platform' + +import bitrix, { bitrixId } from '@hcengineering/bitrix' +import { Ref } from '@hcengineering/core' +import { Handler, IntegrationType } from '@hcengineering/setting' +import { AnyComponent } from '@hcengineering/ui' + +export default mergeIds(bitrixId, bitrix, { + string: { + BitrixTokenUrl: '' as IntlString, + Bitrix: '' as IntlString, + BitrixDesc: '' as IntlString, + Settings: '' as IntlString, + EntityMapping: '' as IntlString, + NotAllowed: '' as IntlString, + AddMapping: '' as IntlString, + BitrixEntityType: '' as IntlString, + FieldMapping: '' as IntlString, + AddField: '' as IntlString, + Attribute: '' as IntlString, + MapField: '' as IntlString + }, + component: { + BitrixIcon: '' as AnyComponent + }, + handler: { + DisconnectHandler: '' as Handler + }, + integrationType: { + Bitrix: '' as Ref + } +}) diff --git a/plugins/bitrix-resources/src/types.ts b/plugins/bitrix-resources/src/types.ts new file mode 100644 index 0000000000..c105c9b3c6 --- /dev/null +++ b/plugins/bitrix-resources/src/types.ts @@ -0,0 +1,45 @@ +/** + * @public + */ +export interface BitrixProfile { + ID: string + ADMIN: boolean + NAME: string + LAST_NAME: string + PERSONAL_GENDER: string + PERSONAL_PHOTO: string + TIME_ZONE: string + TIME_ZONE_OFFSET: number +} + +export type NumberString = string +export type ISODate = string +export type BoolString = 'Y' | 'N' +export type GenderString = 'M' | 'F' | '' + +export interface MultiField { + readonly ID: NumberString + readonly VALUE_TYPE: string + readonly VALUE: string + readonly TYPE_ID: string +} +export type MultiFieldArray = ReadonlyArray> + +export interface StatusValue { + CATEGORY_ID: string | null + COLOR: string | null + ENTITY_ID: string | null + ID: number + NAME: string + NAME_INIT: string | null + SEMANTICS: string | null + SORT: string | null + STATUS_ID: string | null + SYSTEM: 'Y' | 'N' +} + +export interface BitrixResult { + result: any + next: number + total: number +} diff --git a/plugins/bitrix-resources/src/utils.ts b/plugins/bitrix-resources/src/utils.ts new file mode 100644 index 0000000000..70b039bce4 --- /dev/null +++ b/plugins/bitrix-resources/src/utils.ts @@ -0,0 +1,277 @@ +import { + BitrixEntityMapping, + BitrixFieldMapping, + BitrixSyncDoc, + CopyValueOperation, + CreateChannelOperation, + CreateTagOperation, + MappingOperation +} from '@hcengineering/bitrix' +import { Comment } from '@hcengineering/chunter' +import contact, { Channel, EmployeeAccount } from '@hcengineering/contact' +import core, { AnyAttribute, Class, Client, Data, Doc, generateId, Mixin, Ref, Space } from '@hcengineering/core' +import tags, { TagElement, TagReference } from '@hcengineering/tags' +import { getColorNumberByText } from '@hcengineering/ui' + +export function collectFields (fieldMapping: BitrixFieldMapping[]): string[] { + const fields: string[] = ['ID'] + for (const f of fieldMapping) { + switch (f.operation.kind) { + case MappingOperation.CopyValue: + fields.push( + ...Array.from(f.operation.patterns.map((it) => it.field).filter((it) => it !== undefined) as string[]) + ) + break + case MappingOperation.CreateChannel: + fields.push(...Array.from(f.operation.fields.map((it) => it.field).filter((it) => it !== undefined))) + break + case MappingOperation.CreateTag: + fields.push(...Array.from(f.operation.fields.map((it) => it.field).filter((it) => it !== undefined))) + break + } + } + return fields +} + +export interface ConvertResult { + document: BitrixSyncDoc // Document we should achive + mixins: Record>, Data> // Mixins of document we will achive + extraDocs: Doc[] // Extra documents we will achive, comments etc. + blobs: File[] // + comments?: Promise> +} + +export async function convert ( + client: Client, + entity: BitrixEntityMapping, + space: Ref, + 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>, TagElement[]>, // TagElement cache. + userList: Map> +): Promise { + const hierarchy = client.getHierarchy() + const document: BitrixSyncDoc = { + _id: generateId(), + type: entity.type, + bitrixId: `${rawDocument.ID as string}`, + _class: entity.ofClass, + space, + modifiedOn: new Date(rawDocument.DATE_CREATE).getTime(), + modifiedBy: userList.get(rawDocument.CREATED_BY_ID) ?? core.account.System + } + + // Obtain a proper modified by for document + + const newExtraDocs: Doc[] = [] + const blobs: File[] = [] + const mixins: Record>, Data> = {} + + const extractValue = (field?: string, alternatives?: string[]): any | undefined => { + if (field !== undefined) { + let lval = rawDocument[field] + if ((lval == null || lval === '') && alternatives !== undefined) { + for (const alt of alternatives) { + lval = rawDocument[alt] + if (lval != null) { + break + } + } + } + const bfield = entity.bitrixFields[field] + if (bfield === undefined) { + console.trace('Bitrix field not found', field) + } else if (bfield.type === 'integer' || bfield.type === 'double') { + if (bfield.isMultiple && Array.isArray(lval)) { + return lval[0] ?? 0 + } + return lval + } else if (bfield.type === 'crm_multifield') { + if (Array.isArray(lval)) { + return lval.map((it) => it.VALUE) + } + } else if (bfield.type === 'string' || bfield.type === 'url') { + if (bfield.isMultiple && Array.isArray(lval)) { + return lval.join(', ') + } + return lval + } else if (bfield.type === 'date') { + if (lval !== '' && lval != null) { + return new Date(lval) + } + } 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 + } + } + } + } + } + + const getCopyValue = async (attr: AnyAttribute, operation: CopyValueOperation): Promise => { + const r: Array = [] + for (const o of operation.patterns) { + if (o.text.length > 0) { + r.push(o.text) + } + const lval = extractValue(o.field, o.alternatives) + if (lval != null) { + r.push(lval) + } + } + if (r.length === 1) { + return r[0] + } + if (r.length === 0) { + return + } + return r.join('').trim() + } + + const getChannelValue = async (attr: AnyAttribute, operation: CreateChannelOperation): Promise => { + for (const f of operation.fields) { + const lval = extractValue(f.field) + if (lval != null && lval !== '') { + const vals = Array.isArray(lval) ? lval : [lval] + for (const llVal of vals) { + const c: Channel = { + _id: generateId(), + _class: contact.class.Channel, + attachedTo: document._id, + attachedToClass: attr.attributeOf, + collection: attr.name, + modifiedBy: document.modifiedBy, + value: llVal, + provider: f.provider, + space: document.space, + modifiedOn: document.modifiedOn + } + newExtraDocs.push(c) + } + } + } + return undefined + } + + const getTagValue = async (attr: AnyAttribute, operation: CreateTagOperation): Promise => { + const elements = + tagElements.get(attr.attributeOf) ?? + (await client.findAll(tags.class.TagElement, { + targetClass: attr.attributeOf + })) + + const references = await client.findAll(tags.class.TagReference, { + attachedTo: document._id + }) + // 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 = await client.findOne(tags.class.TagCategory, { + targetClass: attr.attributeOf, + default: true + }) + if (defaultCategory === undefined) { + console.error('could not proceed tags without default category') + return + } + for (const o of operation.fields) { + const lval = extractValue(o.field) + let vals: string[] = [] + if (lval == null) { + continue + } + if (o.split !== '' && o.split != null) { + vals = `${lval as string}`.split(o.split) + } else { + vals = [lval as string] + } + 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) + if (tag === undefined) { + tag = { + _id: generateId(), + _class: tags.class.TagElement, + category: defaultCategory._id, + color: getColorNumberByText(vv), + description: '', + title: vv, + targetClass: attr.attributeOf, + space: tags.space.Tags, + modifiedBy: document.modifiedBy, + modifiedOn: document.modifiedOn + } + newExtraDocs.push(tag) + } + const ref: TagReference = { + _id: generateId(), + attachedTo: document._id, + attachedToClass: attr.attributeOf, + collection: attr.name, + _class: tags.class.TagReference, + tag: tag._id, + color: getColorNumberByText(vv), + 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) + } + } + } + return undefined + } + + for (const f of fields) { + const attr = hierarchy.getAttribute(f.ofClass, f.attributeName) + if (attr === undefined) { + console.trace('Attribue not found', f) + continue + } + let value: any + switch (f.operation.kind) { + case MappingOperation.CopyValue: + value = await getCopyValue(attr, f.operation) + break + case MappingOperation.CreateChannel: + value = await getChannelValue(attr, f.operation) + break + case MappingOperation.CreateTag: + value = await getTagValue(attr, f.operation) + break + } + if (value !== undefined) { + if (hierarchy.isMixin(attr.attributeOf)) { + mixins[attr.attributeOf] = { ...mixins[attr.attributeOf], [attr.name]: value } + } else { + ;(document as any)[attr.name] = value + } + } + } + + return { document, mixins, extraDocs: newExtraDocs, blobs } +} + +/** + * @public + */ +export function toClassRef (val: any): Ref> { + return val as Ref> +} diff --git a/plugins/bitrix-resources/svelte.config.js b/plugins/bitrix-resources/svelte.config.js new file mode 100644 index 0000000000..944a06f73e --- /dev/null +++ b/plugins/bitrix-resources/svelte.config.js @@ -0,0 +1,5 @@ +const sveltePreprocess = require('svelte-preprocess') + +module.exports = { + preprocess: sveltePreprocess() +}; \ No newline at end of file diff --git a/plugins/bitrix-resources/tsconfig.json b/plugins/bitrix-resources/tsconfig.json new file mode 100644 index 0000000000..0980a1b8b4 --- /dev/null +++ b/plugins/bitrix-resources/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "esnext", + "module": "esnext", + "declaration": true, + "outDir": "./lib", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "lib": [ + "esnext", + "dom" + ] + } +} \ No newline at end of file diff --git a/plugins/bitrix/.eslintrc.js b/plugins/bitrix/.eslintrc.js new file mode 100644 index 0000000000..7f7cebe58b --- /dev/null +++ b/plugins/bitrix/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/config/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/plugins/bitrix/.npmignore b/plugins/bitrix/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/plugins/bitrix/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/plugins/bitrix/config/rig.json b/plugins/bitrix/config/rig.json new file mode 100644 index 0000000000..2fdc07ba20 --- /dev/null +++ b/plugins/bitrix/config/rig.json @@ -0,0 +1,18 @@ +// The "rig.json" file directs tools to look for their config files in an external package. +// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + /** + * (Required) The name of the rig package to inherit from. + * It should be an NPM package name with the "-rig" suffix. + */ + "rigPackageName": "@hcengineering/platform-rig" + + /** + * (Optional) Selects a config profile from the rig package. The name must consist of + * lowercase alphanumeric words separated by hyphens, for example "sample-profile". + * If omitted, then the "default" profile will be used." + */ + // "rigProfile": "your-profile-name" +} diff --git a/plugins/bitrix/package.json b/plugins/bitrix/package.json new file mode 100644 index 0000000000..08a3e8c728 --- /dev/null +++ b/plugins/bitrix/package.json @@ -0,0 +1,36 @@ +{ + "name": "@hcengineering/bitrix", + "version": "0.6.0", + "main": "lib/index.js", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "heft build", + "build:watch": "tsc", + "lint:fix": "eslint --fix src", + "lint": "eslint src", + "format": "prettier --write src && eslint --fix src" + }, + "devDependencies": { + "@hcengineering/platform-rig": "~0.6.0", + "@types/heft-jest": "^1.0.3", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.26.0", + "@typescript-eslint/parser": "^5.41.0", + "eslint-config-standard-with-typescript": "^23.0.0", + "prettier": "^2.7.1", + "@rushstack/heft": "^0.47.9", + "typescript": "^4.3.5" + }, + "dependencies": { + "@hcengineering/platform": "^0.6.8", + "@hcengineering/core": "^0.6.19", + "@hcengineering/ui": "^0.6.2", + "@hcengineering/preference": "^0.6.1", + "@hcengineering/tags": "~0.6.2", + "@hcengineering/contact": "~0.6.8" + } +} diff --git a/plugins/bitrix/src/index.ts b/plugins/bitrix/src/index.ts new file mode 100644 index 0000000000..50143c0075 --- /dev/null +++ b/plugins/bitrix/src/index.ts @@ -0,0 +1,184 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { ChannelProvider } from '@hcengineering/contact' +import type { AttachedDoc, Class, Doc, Mixin, Ref, Space } from '@hcengineering/core' +import type { Plugin } from '@hcengineering/platform' +import { Asset, plugin } from '@hcengineering/platform' +import { ExpertKnowledge, InitialKnowledge, MeaningfullKnowledge } from '@hcengineering/tags' +import { AnyComponent } from '@hcengineering/ui' + +/** + * @public + */ +export interface BitrixSyncDoc extends Doc { + type: string + bitrixId: string +} + +/** + * @public + */ +export enum BitrixEntityType { + Comment = 'crm.timeline.comment', + Binding = 'crm.timeline.bindings', + Lead = 'crm.lead', + Activity = 'crm.activity', + Company = 'crm.company' +} + +/** + * @public + */ +export const mappingTypes = [ + { label: 'Leads', id: BitrixEntityType.Lead }, + // { label: 'Comments', id: BitrixEntityType.Comment }, + { label: 'Company', id: BitrixEntityType.Company } + // { label: 'Activity', id: BitrixEntityType.Activity } +] + +/** + * @public + */ +export interface FieldValue { + type: string + statusType?: string + isRequired: boolean + isReadOnly: boolean + isImmutable: boolean + isMultiple: boolean + isDynamic: boolean + title: string + + formLabel?: string + filterLabel?: string + items?: Array<{ + ID: string + VALUE: string + }> +} + +/** + * @public + */ +export interface Fields { + [key: string]: FieldValue +} + +/** + * @public + */ +export interface BitrixEntityMapping extends Doc { + ofClass: Ref> + type: string + bitrixFields: Fields + + fields: number + + comments: boolean + activity: boolean + attachments: boolean +} +/** + * @public + */ +export enum MappingOperation { + CopyValue, + CreateTag, // Create tag + CreateChannel // Create channel +} +/** + * @public + */ +export interface CopyPattern { + text: string + field?: string + alternatives?: string[] +} +/** + * @public + */ +export interface CopyValueOperation { + kind: MappingOperation.CopyValue + patterns: CopyPattern[] +} + +/** + * @public + */ +export interface TagField { + weight: InitialKnowledge | MeaningfullKnowledge | ExpertKnowledge + + field: string + split: string // If defined values from field will be split to check for multiple values. +} +/** + * @public + */ +export interface CreateTagOperation { + kind: MappingOperation.CreateTag + + fields: TagField[] +} + +/** + * @public + */ +export interface ChannelFieldMapping { + provider: Ref + field: string +} + +/** + * @public + */ +export interface CreateChannelOperation { + kind: MappingOperation.CreateChannel + fields: ChannelFieldMapping[] +} + +/** + * @public + */ +export interface BitrixFieldMapping extends AttachedDoc { + ofClass: Ref> // Specify mixin if applicable + attributeName: string + + operation: CopyValueOperation | CreateTagOperation | CreateChannelOperation +} + +/** + * @public + */ +export const bitrixId = 'bitrix' as Plugin + +export default plugin(bitrixId, { + mixin: { + BitrixSyncDoc: '' as Ref> + }, + class: { + EntityMapping: '' as Ref>, + FieldMapping: '' as Ref> + }, + component: { + BitrixIntegration: '' as AnyComponent + }, + icon: { + Bitrix: '' as Asset + }, + space: { + Mappings: '' as Ref + } +}) diff --git a/plugins/bitrix/tsconfig.json b/plugins/bitrix/tsconfig.json new file mode 100644 index 0000000000..fd821f3e97 --- /dev/null +++ b/plugins/bitrix/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "lib": ["esnext", "dom"] + } +} \ No newline at end of file diff --git a/plugins/setting-assets/lang/en.json b/plugins/setting-assets/lang/en.json index f80e06a53c..e772b933c4 100644 --- a/plugins/setting-assets/lang/en.json +++ b/plugins/setting-assets/lang/en.json @@ -66,6 +66,7 @@ "ShowAttribute": "Show attribute", "HideAttribute": "Hide attribute", "Visibility": "Visibility", - "Hidden": "Hidden" + "Hidden": "Hidden", + "Configure": "Configure" } } \ No newline at end of file diff --git a/plugins/setting-assets/lang/ru.json b/plugins/setting-assets/lang/ru.json index d8fd1311ea..b41151f18e 100644 --- a/plugins/setting-assets/lang/ru.json +++ b/plugins/setting-assets/lang/ru.json @@ -67,6 +67,7 @@ "ShowAttribute": "Hide", "HideAttribute": "Show", "Visibility": "Видимость", - "Hidden": "Спрятанный" + "Hidden": "Спрятанный", + "Configure": "Настроить..." } } \ No newline at end of file diff --git a/plugins/setting-resources/src/components/ClassAttributes.svelte b/plugins/setting-resources/src/components/ClassAttributes.svelte index 4b2da069cb..e38523a98a 100644 --- a/plugins/setting-resources/src/components/ClassAttributes.svelte +++ b/plugins/setting-resources/src/components/ClassAttributes.svelte @@ -31,6 +31,7 @@ import { Action, ActionIcon, + AnySvelteComponent, CircleButton, Component, getEventPositionElement, @@ -51,6 +52,15 @@ import EditClassLabel from './EditClassLabel.svelte' export let _class: Ref> + export let ofClass: Ref> | undefined + + export let attributeMapper: + | { + component: AnySvelteComponent + label: IntlString + props: Record + } + | undefined const client = getClient() const hierarchy = client.getHierarchy() @@ -66,7 +76,9 @@ function getCustomAttributes (_class: Ref>): AnyAttribute[] { const cl = hierarchy.getClass(_class) - const attributes = Array.from(hierarchy.getAllAttributes(_class, cl.extends).values()) + const attributes = Array.from( + hierarchy.getAllAttributes(_class, _class === ofClass ? core.class.Doc : cl.extends).values() + ) // const filtred = attributes.filter((p) => !p.hidden) return attributes } @@ -210,6 +222,11 @@