From 797e3f9bab319d6dd9ea9f73d703c24bdcdfb55f Mon Sep 17 00:00:00 2001 From: Joan Gallego Girona Date: Mon, 30 Sep 2024 17:04:17 +0200 Subject: [PATCH 1/3] WIP, changes to actually make it work on the dummy service (#7283) * WIP, changes to actually make it work on the dummy service * Added option to deactivate the service * Removed unused includes * Included ATConfig injection * Fixed typing errors * Removed access to async context before tenant * Delete ATSemanticConfig.json * Upped version. * Added environmnent to test. --------- Co-authored-by: RafaPolit --- app/api/eventListeners.ts | 2 + .../AutomaticTranslationFactory.ts | 6 + .../RequestEntityTranslation.ts | 32 ++- .../driving/ATEntityCreationListener.ts | 27 ++- .../adapters/driving/ATServiceListener.ts | 4 +- .../specs/ATEntityCreatedListener.spec.ts | 59 ++++- .../driving/specs/ATServiceListener.spec.ts | 8 +- .../specs/RequestEntityTranslation.spec.ts | 20 +- app/api/utils/handleError.js | 1 + app/react/App/styles/globals.css | 218 +++++++++--------- app/worker.ts | 2 + package.json | 2 +- 12 files changed, 217 insertions(+), 164 deletions(-) diff --git a/app/api/eventListeners.ts b/app/api/eventListeners.ts index 2daaddc844..ced38d48fe 100644 --- a/app/api/eventListeners.ts +++ b/app/api/eventListeners.ts @@ -1,10 +1,12 @@ import { EventsBus } from './eventsbus'; +import { AutomaticTranslationFactory } from './externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory'; import { registerEventListeners as registerSegmentationListeners } from './services/pdfsegmentation/eventListeners'; import { Suggestions } from './suggestions/suggestions'; const registerEventListeners = (eventsBus: EventsBus) => { Suggestions.registerEventListeners(eventsBus); registerSegmentationListeners(eventsBus); + AutomaticTranslationFactory.defaultATEntityCreationListener(eventsBus).start(); }; export { registerEventListeners }; diff --git a/app/api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory.ts b/app/api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory.ts index 7315d3bdc6..d56d6ba1bd 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory.ts @@ -1,10 +1,12 @@ import { DefaultTransactionManager } from 'api/common.v2/database/data_source_defaults'; import { getConnection } from 'api/common.v2/database/getConnectionForCurrentTenant'; import { DefaultEntitiesDataSource } from 'api/entities.v2/database/data_source_defaults'; +import { EventsBus } from 'api/eventsbus'; import { TaskManager } from 'api/services/tasksmanager/TaskManager'; import { DefaultSettingsDataSource } from 'api/settings.v2/database/data_source_defaults'; import { DefaultTemplatesDataSource } from 'api/templates.v2/database/data_source_defaults'; import { MongoTemplatesDataSource } from 'api/templates.v2/database/MongoTemplatesDataSource'; +import { ATEntityCreationListener } from './adapters/driving/ATEntityCreationListener'; import { GenerateAutomaticTranslationsCofig } from './GenerateAutomaticTranslationConfig'; import { AJVATConfigValidator } from './infrastructure/AJVATConfigValidator'; import { AJVTranslationResultValidator } from './infrastructure/AJVTranslationResultValidator'; @@ -53,6 +55,10 @@ const AutomaticTranslationFactory = { new AJVEntityInputValidator() ); }, + + defaultATEntityCreationListener(eventsBus: EventsBus) { + return new ATEntityCreationListener(eventsBus); + }, }; export { AutomaticTranslationFactory }; diff --git a/app/api/externalIntegrations.v2/automaticTranslation/RequestEntityTranslation.ts b/app/api/externalIntegrations.v2/automaticTranslation/RequestEntityTranslation.ts index f0d25f9aef..af9464cf91 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/RequestEntityTranslation.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/RequestEntityTranslation.ts @@ -7,16 +7,14 @@ import { ATConfigService } from './services/GetAutomaticTranslationConfig'; import { InvalidInputDataFormat } from './errors/generateATErrors'; export type ATTaskMessage = { - params: { - key: string[]; - text: string; - language_from: string; - languages_to: string[]; - }; + key: string[]; + text: string; + language_from: string; + languages_to: string[]; }; export class RequestEntityTranslation { - static SERVICE_NAME = 'AutomaticTranslation'; + static SERVICE_NAME = 'translations'; private taskManager: TaskManager; @@ -74,12 +72,10 @@ export class RequestEntityTranslation { } await this.taskManager.startTask({ - params: { - key: [getTenant().name, entity.sharedId, commonPropName], - text: entity[commonPropName], - language_from: languageFrom, - languages_to: languagesTo, - }, + key: [getTenant().name, entity.sharedId, commonPropId.toString()], + text: entity[commonPropName], + language_from: languageFrom, + languages_to: languagesTo, }); }); atTemplateConfig?.properties.forEach(async propId => { @@ -94,12 +90,10 @@ export class RequestEntityTranslation { if (entity.metadata[propName]?.[0].value) { await this.taskManager.startTask({ - params: { - key: [getTenant().name, entity.sharedId, propName], - text: entity.metadata[propName][0].value, - language_from: languageFrom, - languages_to: languagesTo, - }, + key: [getTenant().name, entity.sharedId, propId], + text: entity.metadata[propName][0].value, + language_from: languageFrom, + languages_to: languagesTo, }); } }); diff --git a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATEntityCreationListener.ts b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATEntityCreationListener.ts index 502d95e0c7..1932a82376 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATEntityCreationListener.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATEntityCreationListener.ts @@ -1,25 +1,34 @@ import { EntityCreatedEvent } from 'api/entities/events/EntityCreatedEvent'; import { EventsBus } from 'api/eventsbus'; -import { RequestEntityTranslation } from '../../RequestEntityTranslation'; +import { permissionsContext } from 'api/permissions/permissionsContext'; +import { AutomaticTranslationFactory } from '../../AutomaticTranslationFactory'; export class ATEntityCreationListener { - private requestEntityTranslation: RequestEntityTranslation; - private eventBus: EventsBus; - constructor(requestEntityTranslation: RequestEntityTranslation, eventBus: EventsBus) { - this.requestEntityTranslation = requestEntityTranslation; + private ATFactory: typeof AutomaticTranslationFactory; + + constructor( + eventBus: EventsBus, + ATFactory: typeof AutomaticTranslationFactory = AutomaticTranslationFactory + ) { this.eventBus = eventBus; + this.ATFactory = ATFactory; } start() { this.eventBus.on(EntityCreatedEvent, async event => { - const entityFrom = event.entities.find(e => e.language === event.targetLanguageKey) || {}; + const { active } = await this.ATFactory.defaultATConfigService().get(); + + if (active) { + permissionsContext.setCommandContext(); + const entityFrom = event.entities.find(e => e.language === event.targetLanguageKey) || {}; - entityFrom._id = entityFrom._id?.toString(); - entityFrom.template = entityFrom.template?.toString(); + entityFrom._id = entityFrom._id?.toString(); + entityFrom.template = entityFrom.template?.toString(); - await this.requestEntityTranslation.execute(entityFrom); + await this.ATFactory.defaultRequestEntityTranslation().execute(entityFrom); + } }); } } diff --git a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATServiceListener.ts b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATServiceListener.ts index 744a98cee8..a1148c3e5c 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATServiceListener.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATServiceListener.ts @@ -1,12 +1,13 @@ import { tenants } from 'api/tenants'; import { TaskManager } from 'api/services/tasksmanager/TaskManager'; +import { permissionsContext } from 'api/permissions/permissionsContext'; import { ATTranslationResultValidator } from '../../contracts/ATTranslationResultValidator'; import { AJVTranslationResultValidator } from '../../infrastructure/AJVTranslationResultValidator'; import { InvalidATServerResponse } from '../../errors/generateATErrors'; import { AutomaticTranslationFactory } from '../../AutomaticTranslationFactory'; export class ATServiceListener { - static SERVICE_NAME = 'AutomaticTranslation'; + static SERVICE_NAME = 'translations'; private taskManager: TaskManager; @@ -20,6 +21,7 @@ export class ATServiceListener { } await tenants.run(async () => { + permissionsContext.setCommandContext(); await ATFactory.defaultSaveEntityTranslations().execute(result); }, result.key[0]); }, diff --git a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATEntityCreatedListener.spec.ts b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATEntityCreatedListener.spec.ts index 6113681746..35e04ce5d6 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATEntityCreatedListener.spec.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATEntityCreatedListener.spec.ts @@ -4,25 +4,52 @@ import { RequestEntityTranslation } from 'api/externalIntegrations.v2/automaticT import { testingEnvironment } from 'api/utils/testingEnvironment'; import { AutomaticTranslationFactory } from 'api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory'; import { getFixturesFactory } from 'api/utils/fixturesFactory'; +import { tenants } from 'api/tenants'; +import { DefaultTransactionManager } from 'api/common.v2/database/data_source_defaults'; +import { ATConfigService } from 'api/externalIntegrations.v2/automaticTranslation/services/GetAutomaticTranslationConfig'; +import { DefaultSettingsDataSource } from 'api/settings.v2/database/data_source_defaults'; +import { MongoATConfigDataSource } from 'api/externalIntegrations.v2/automaticTranslation/infrastructure/MongoATConfigDataSource'; +import { getConnection } from 'api/common.v2/database/getConnectionForCurrentTenant'; +import { DefaultTemplatesDataSource } from 'api/templates.v2/database/data_source_defaults'; +import { ATExternalAPI } from 'api/externalIntegrations.v2/automaticTranslation/infrastructure/ATExternalAPI'; import { ATEntityCreationListener } from '../ATEntityCreationListener'; const factory = getFixturesFactory(); +const prepareATFactory = (executeSpy: jest.Mock) => { + // @ts-ignore + const ATFactory: typeof AutomaticTranslationFactory = { + defaultATConfigService() { + const transactionManager = DefaultTransactionManager(); + return new ATConfigService( + DefaultSettingsDataSource(transactionManager), + new MongoATConfigDataSource(getConnection(), transactionManager), + DefaultTemplatesDataSource(transactionManager), + new ATExternalAPI() + ); + }, + defaultRequestEntityTranslation() { + return { execute: executeSpy } as unknown as RequestEntityTranslation; + }, + }; + + return ATFactory; +}; + describe('ATEntityCreationListener', () => { let listener: ATEntityCreationListener; const eventBus: EventsBus = new EventsBus(); - let requestEntityTranslation: RequestEntityTranslation; + let executeSpy: jest.Mock; beforeEach(async () => { await testingEnvironment.setUp({ - settings: [{ features: { automaticTranslation: { active: true } } }], + settings: [{ features: { automaticTranslation: { active: false } } }], }); await testingEnvironment.setTenant('tenant'); - requestEntityTranslation = AutomaticTranslationFactory.defaultRequestEntityTranslation(); - jest.spyOn(requestEntityTranslation, 'execute'); + executeSpy = jest.fn(); - listener = new ATEntityCreationListener(requestEntityTranslation, eventBus); + listener = new ATEntityCreationListener(eventBus, prepareATFactory(executeSpy)); listener.start(); }); @@ -31,16 +58,34 @@ describe('ATEntityCreationListener', () => { }); describe('Request entity translation', () => { + it('should not request translations if feature flag is off', async () => { + const entityCreationEvent = new EntityCreatedEvent({ + entities: [{ sharedId: 'entity 1' }], + targetLanguageKey: 'en', + }); + + await tenants.run(async () => { + await eventBus.emit(entityCreationEvent); + }, 'tenant'); + + expect(executeSpy).not.toHaveBeenCalled(); + }); + it('should call RequestEntityTranslation on receiving entity creation event', async () => { + await testingEnvironment.setFixtures({ + settings: [{ features: { automaticTranslation: { active: true } } }], + }); const entityEn = factory.entity('entity1', 'template1', {}, { language: 'en' }); const entityCreationEvent = new EntityCreatedEvent({ entities: [factory.entity('entity1', 'template1', {}, { language: 'es' }), entityEn], targetLanguageKey: 'en', }); - await eventBus.emit(entityCreationEvent); + await tenants.run(async () => { + await eventBus.emit(entityCreationEvent); + }, 'tenant'); - expect(requestEntityTranslation.execute).toHaveBeenCalledWith(entityEn); + expect(executeSpy).toHaveBeenCalledWith(entityEn); }); }); }); diff --git a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATServiceListener.spec.ts b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATServiceListener.spec.ts index 1a18cb2d68..d596a9ad91 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATServiceListener.spec.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/adapters/driving/specs/ATServiceListener.spec.ts @@ -5,16 +5,12 @@ import RedisSMQ from 'rsmq'; import waitForExpect from 'wait-for-expect'; import { AutomaticTranslationFactory } from 'api/externalIntegrations.v2/automaticTranslation/AutomaticTranslationFactory'; import { testingEnvironment } from 'api/utils/testingEnvironment'; -import { GenerateAutomaticTranslationsCofig } from 'api/externalIntegrations.v2/automaticTranslation/GenerateAutomaticTranslationConfig'; import { SaveEntityTranslations } from 'api/externalIntegrations.v2/automaticTranslation/SaveEntityTranslations'; import { ATServiceListener } from '../ATServiceListener'; const prepareATFactory = (executeSpy: jest.Mock) => { // @ts-ignore const ATFactory: typeof AutomaticTranslationFactory = { - defaultGenerateATConfig() { - return {} as GenerateAutomaticTranslationsCofig; - }, defaultSaveEntityTranslations() { return { execute: executeSpy } as unknown as SaveEntityTranslations; }, @@ -57,7 +53,7 @@ describe('ATServiceListener', () => { await redisSMQ.createQueueAsync({ qname: queueName }); }; - await recreateQueue('AutomaticTranslation_results').catch(error => { + await recreateQueue(`${ATServiceListener.SERVICE_NAME}_results`).catch(error => { throw error; }); @@ -85,7 +81,7 @@ describe('ATServiceListener', () => { executeSpy.mockClear(); await redisSMQ.sendMessageAsync({ - qname: 'AutomaticTranslation_results', + qname: `${config.ENVIRONMENT}_${ATServiceListener.SERVICE_NAME}_results`, message: JSON.stringify(message), }); diff --git a/app/api/externalIntegrations.v2/automaticTranslation/specs/RequestEntityTranslation.spec.ts b/app/api/externalIntegrations.v2/automaticTranslation/specs/RequestEntityTranslation.spec.ts index 2b762cb80d..7a1ef6f342 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/specs/RequestEntityTranslation.spec.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/specs/RequestEntityTranslation.spec.ts @@ -87,21 +87,17 @@ describe('RequestEntityTranslation', () => { expect(taskManager.startTask).toHaveBeenCalledTimes(2); expect(taskManager.startTask).toHaveBeenCalledWith({ - params: { - key: ['tenant', 'entity1', 'title'], - text: 'entity1', - language_from: 'en', - languages_to: ['es'], - }, + key: ['tenant', 'entity1', factory.commonPropertiesTitleId('template1').toString()], + text: 'entity1', + language_from: 'en', + languages_to: ['es'], }); expect(taskManager.startTask).toHaveBeenCalledWith({ - params: { - key: ['tenant', 'entity1', 'text1'], - text: 'original text1', - language_from: 'en', - languages_to: ['es'], - }, + key: ['tenant', 'entity1', factory.property('text1')._id?.toString()], + text: 'original text1', + language_from: 'en', + languages_to: ['es'], }); }); diff --git a/app/api/utils/handleError.js b/app/api/utils/handleError.js index 044121352a..875c700c19 100644 --- a/app/api/utils/handleError.js +++ b/app/api/utils/handleError.js @@ -69,6 +69,7 @@ const prettifyError = (error, { req = {}, uncaught = false } = {}) => { } if (error instanceof Ajv.ValidationError) { + console.log(util.inspect(error)); result = { code: 422, message: error.message, validations: error.errors, logLevel: 'debug' }; } diff --git a/app/react/App/styles/globals.css b/app/react/App/styles/globals.css index 503dc0a90c..beb79f6b66 100644 --- a/app/react/App/styles/globals.css +++ b/app/react/App/styles/globals.css @@ -1,5 +1,113 @@ +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(99 102 241 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(99 102 241 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + /* -! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com +! tailwindcss v3.4.12 | MIT License | https://tailwindcss.com */ /* @@ -887,114 +995,6 @@ input[type="range"]::-ms-fill-lower { font-family: 'Inter', sans-serif !important; } -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(99 102 241 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(99 102 241 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - .container { width: 100%; } diff --git a/app/worker.ts b/app/worker.ts index d0cc7aeb02..c21548f498 100644 --- a/app/worker.ts +++ b/app/worker.ts @@ -13,6 +13,7 @@ import { InformationExtraction } from 'api/services/informationextraction/Inform import { setupWorkerSockets } from 'api/socketio/setupSockets'; import { ConvertToPdfWorker } from 'api/services/convertToPDF/ConvertToPdfWorker'; import { handleError } from './api/utils/handleError.js'; +import { ATServiceListener } from 'api/externalIntegrations.v2/automaticTranslation/adapters/driving/ATServiceListener'; let dbAuth = {}; @@ -42,6 +43,7 @@ DB.connect(config.DBHOST, dbAuth) console.info('==> 📡 starting external services...'); ocrManager.start(); + new ATServiceListener().start(); new InformationExtraction().start(); new ConvertToPdfWorker().start(); diff --git a/package.json b/package.json index 218894df4a..14a02bf558 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uwazi", - "version": "1.186.0", + "version": "1.186.1", "description": "Uwazi is a free, open-source solution for organising, analysing and publishing your documents.", "keywords": [ "react" From 830db6ff8f29266655b622cb8dd4df41ae6d01b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20P=C3=B3lit?= Date: Thu, 3 Oct 2024 06:41:56 -0500 Subject: [PATCH 2/3] Be able to Automatic Translate Title (#7288) --- .../contracts/EntitiesDataSource.ts | 3 +- .../database/MongoEntitiesDataSource.ts | 5 ++- .../SaveEntityTranslations.ts | 33 ++++++++++++++----- .../specs/SaveEntityTranslations.spec.ts | 31 +++++++++++++++++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/app/api/entities.v2/contracts/EntitiesDataSource.ts b/app/api/entities.v2/contracts/EntitiesDataSource.ts index 5f637a26e7..f31c363cd7 100644 --- a/app/api/entities.v2/contracts/EntitiesDataSource.ts +++ b/app/api/entities.v2/contracts/EntitiesDataSource.ts @@ -12,7 +12,8 @@ export interface EntitiesDataSource { ): Promise; updateMetadataValues( id: Entity['_id'], - values: Record + values: Record, + title?: string ): Promise; entitiesExist(sharedIds: string[]): Promise; getByIds(sharedIds: string[], language?: string): ResultSet; diff --git a/app/api/entities.v2/database/MongoEntitiesDataSource.ts b/app/api/entities.v2/database/MongoEntitiesDataSource.ts index 3a2de5c661..61635e415e 100644 --- a/app/api/entities.v2/database/MongoEntitiesDataSource.ts +++ b/app/api/entities.v2/database/MongoEntitiesDataSource.ts @@ -127,7 +127,8 @@ export class MongoEntitiesDataSource // eslint-disable-next-line class-methods-use-this async updateMetadataValues( id: Entity['_id'], - values: Record + values: Record, + title?: string ) { // This is using V1 so that it gets denormalized to speed up development // this is a hack and should be changed as soon as we finish AT @@ -136,6 +137,8 @@ export class MongoEntitiesDataSource throw new Error(`entity does not exists: ${id}`); } + entityToModify.title = title || entityToModify.title; + Object.entries(values).forEach(([propertyName, metadataValues]) => { entityToModify.metadata = entityToModify.metadata || {}; // @ts-ignore diff --git a/app/api/externalIntegrations.v2/automaticTranslation/SaveEntityTranslations.ts b/app/api/externalIntegrations.v2/automaticTranslation/SaveEntityTranslations.ts index 06671d0f44..91169687b8 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/SaveEntityTranslations.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/SaveEntityTranslations.ts @@ -30,7 +30,7 @@ export class SaveEntityTranslations { const [, entitySharedId, propertyId] = translationResult.key; - const property = await this.getProperty(entitySharedId, propertyId); + const { property, scope } = await this.getProperty(entitySharedId, propertyId); const entities = this.entitiesDS.getByIds([entitySharedId]); @@ -38,16 +38,26 @@ export class SaveEntityTranslations { const translation = translationResult.translations.find( t => t.language === oneEntity.language ); - if (translation) { - await this.entitiesDS.updateMetadataValues(oneEntity._id, { - [property.name]: [ - { value: `${SaveEntityTranslations.AITranslatedText} ${translation.text}` }, - ], - }); + if (translation && property) { + if (scope === 'metadata') { + await this.entitiesDS.updateMetadataValues(oneEntity._id, { + [property.name]: [ + { value: `${SaveEntityTranslations.AITranslatedText} ${translation.text}` }, + ], + }); + } + if (scope === 'title') { + await this.entitiesDS.updateMetadataValues( + oneEntity._id, + {}, + `${SaveEntityTranslations.AITranslatedText} ${translation.text}` + ); + } } }); } + // eslint-disable-next-line max-statements private async getProperty(entitySharedId: string, propertyId: string) { const entity = await this.entitiesDS.getByIds([entitySharedId]).first(); if (!entity) { @@ -60,10 +70,15 @@ export class SaveEntityTranslations { } const property = template.properties.find(p => p.id === propertyId); + const commonProperty = template.commonProperties.find(p => p.id === propertyId); - if (!property) { + if (!property && !commonProperty) { throw new Error('Property does not exists'); } - return property; + + return { + property: commonProperty || property, + scope: commonProperty ? 'title' : 'metadata', + } as const; } } diff --git a/app/api/externalIntegrations.v2/automaticTranslation/specs/SaveEntityTranslations.spec.ts b/app/api/externalIntegrations.v2/automaticTranslation/specs/SaveEntityTranslations.spec.ts index fd7f7c8a29..6c555ce3b9 100644 --- a/app/api/externalIntegrations.v2/automaticTranslation/specs/SaveEntityTranslations.spec.ts +++ b/app/api/externalIntegrations.v2/automaticTranslation/specs/SaveEntityTranslations.spec.ts @@ -100,6 +100,37 @@ describe('GenerateAutomaticTranslationConfig', () => { }); }); + it('should save translated common properties (title)', async () => { + const translationResult: TranslationResult = { + key: ['tenant', 'entity', factory.commonPropertiesTitleId('template1')], + text: 'entity', + language_from: 'en', + languages_to: ['es', 'pt'], + translations: [ + { text: 'titulo original', language: 'es', success: true, error_message: '' }, + { text: 'titulo original (pt)', language: 'pt', success: true, error_message: '' }, + ], + }; + + await saveEntityTranslations.execute(translationResult); + + const entities = + (await testingDB.mongodb?.collection('entities').find({ sharedId: 'entity' }).toArray()) || + []; + + expect(entities.find(e => e.language === 'es')).toMatchObject({ + title: `${SaveEntityTranslations.AITranslatedText} titulo original`, + }); + + expect(entities.find(e => e.language === 'pt')).toMatchObject({ + title: `${SaveEntityTranslations.AITranslatedText} titulo original (pt)`, + }); + + expect(entities.find(e => e.language === 'en')).toMatchObject({ + title: 'entity', + }); + }); + it('should denormalize text property on related entities', async () => { const fixtures: DBFixture = { settings: [ From ecd9a1f194feb0533df5c7a3d8b208fa71b78f26 Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 3 Oct 2024 13:42:26 +0200 Subject: [PATCH 3/3] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14a02bf558..1b8538ba6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uwazi", - "version": "1.186.1", + "version": "1.186.2", "description": "Uwazi is a free, open-source solution for organising, analysing and publishing your documents.", "keywords": [ "react"