Skip to content

Commit

Permalink
7318 translation pending (#7320)
Browse files Browse the repository at this point in the history
* Removed unnecessary eslint exception

* Renamed spec file to match the tested file

* Renamed describe to match test

* Prepared test, removed eslint exceptions

* Prepared fixture abstraction

* Created SaveEntityTranslationPending use case

* Prepared RequestEntityTranslation to use saveEntityTranslationPending.

* Changed implementation to work with array of languages

* Added logic to save AI Translation pending to Request Entity Translation.

* Renamed file to plural

* Removed standalone use case and included it in Request
  • Loading branch information
RafaPolit authored Oct 9, 2024
1 parent 85ef7af commit 2b09bac
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 65 deletions.
2 changes: 1 addition & 1 deletion app/api/common.v2/database/MongoResultSet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */
import { AggregationCursor, FindCursor } from 'mongodb';
import { BreakLoopSignal, ResultSet } from '../contracts/ResultSet';
import { objectIndex } from 'shared/data_utils/objectIndex';
import { BreakLoopSignal, ResultSet } from '../contracts/ResultSet';

interface MapperFunc<T, U> {
(elem: T): U | Promise<U>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ const AutomaticTranslationFactory = {
},

defaultRequestEntityTranslation() {
const transactionManager = DefaultTransactionManager();
return new RequestEntityTranslation(
new TaskManager<ATTaskMessage>({
serviceName: RequestEntityTranslation.SERVICE_NAME,
}),
AutomaticTranslationFactory.defaultATConfigDataSource(DefaultTransactionManager()),
AutomaticTranslationFactory.defaultATConfigDataSource(transactionManager),
DefaultEntitiesDataSource(transactionManager),
new Validator<EntityInputModel>(entityInputDataSchema),
DefaultLogger()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Entity } from 'api/entities.v2/model/Entity';
import { EntityInputModel } from 'api/entities.v2/types/EntityInputDataType';
import { Logger } from 'api/log.v2/contracts/Logger';
import { TaskManager } from 'api/services/tasksmanager/TaskManager';
import { EntitiesDataSource } from 'api/entities.v2/contracts/EntitiesDataSource';
import { LanguageISO6391 } from 'shared/types/commonTypes';
import { ATConfigDataSource } from './contracts/ATConfigDataSource';
import { Validator } from './infrastructure/Validator';

Expand All @@ -16,22 +18,29 @@ export type ATTaskMessage = {
export class RequestEntityTranslation {
static SERVICE_NAME = 'translations';

private logger: Logger;
static AITranslationPendingText = '(AI translation pending)';

private taskManager: TaskManager<ATTaskMessage>;

private ATConfigDS: ATConfigDataSource;

private entitiesDS: EntitiesDataSource;

private inputValidator: Validator<EntityInputModel>;

private logger: Logger;

// eslint-disable-next-line max-params
constructor(
taskManager: TaskManager<ATTaskMessage>,
ATConfigDS: ATConfigDataSource,
entitiesDS: EntitiesDataSource,
inputValidator: Validator<EntityInputModel>,
logger: Logger
) {
this.taskManager = taskManager;
this.ATConfigDS = ATConfigDS;
this.entitiesDS = entitiesDS;
this.inputValidator = inputValidator;
this.logger = logger;
}
Expand All @@ -58,10 +67,25 @@ export class RequestEntityTranslation {

const entity = Entity.fromInputModel(entityInputModel);

atTemplateConfig?.properties.forEach(async property => {
await atTemplateConfig?.properties.reduce(async (prev, property) => {
await prev;
const propertyValue = entity.getPropertyValue(property);

if (propertyValue) {
const entities = this.entitiesDS.getByIds([entity.sharedId]);
const pendingText = `${RequestEntityTranslation.AITranslationPendingText} ${propertyValue}`;

await entities.forEach(async fetchedEntity => {
if (languagesTo.includes(fetchedEntity.language as LanguageISO6391)) {
await this.entitiesDS.updateEntity(
fetchedEntity.changePropertyValue(property, pendingText)
);
this.logger.info(
`[AT] - Pending translation saved on DB - ${property.name}: ${pendingText}`
);
}
});

await this.taskManager.startTask({
key: [getTenant().name, entity.sharedId, property.id],
text: propertyValue,
Expand All @@ -78,6 +102,6 @@ export class RequestEntityTranslation {
})}`
);
}
});
}, Promise.resolve());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class SaveEntityTranslations {
});
}

// eslint-disable-next-line max-statements
private async getProperty(entitySharedId: string, propertyId: string) {
const entity = await this.entitiesDS.getByIds([entitySharedId]).first();
if (!entity) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* eslint-disable max-classes-per-file */
import { DefaultTransactionManager } from 'api/common.v2/database/data_source_defaults';
import { entityInputDataSchema } from 'api/entities.v2/types/EntityInputDataSchema';
import { EntityInputModel } from 'api/entities.v2/types/EntityInputDataType';
import { Logger } from 'api/log.v2/contracts/Logger';
import { createMockLogger } from 'api/log.v2/infrastructure/MockLogger';
import { TaskManager } from 'api/services/tasksmanager/TaskManager';
import { getFixturesFactory } from 'api/utils/fixturesFactory';
import { DBFixture } from 'api/utils/testing_db';
import testingDB, { DBFixture } from 'api/utils/testing_db';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import { LanguageISO6391 } from 'shared/types/commonTypes';
import { EntitySchema } from 'shared/types/entityType';
import { DefaultEntitiesDataSource } from 'api/entities.v2/database/data_source_defaults';
import { AutomaticTranslationFactory } from '../AutomaticTranslationFactory';
import { ValidationError, Validator } from '../infrastructure/Validator';
import { ATTaskMessage, RequestEntityTranslation } from '../RequestEntityTranslation';
Expand All @@ -19,7 +21,7 @@ const fixtures: DBFixture = {
factory.template('template1', [factory.property('text1'), factory.property('empty_text')]),
],
entities: [
...factory.entityInMultipleLanguages(['en', 'es'], 'entity1', 'template1', {
...factory.entityInMultipleLanguages(['en', 'es', 'pt'], 'entity1', 'template1', {
text1: [{ value: 'original text1' }],
empty_text: [{ value: '' }],
}),
Expand All @@ -29,6 +31,7 @@ const fixtures: DBFixture = {
languages: [
{ label: 'en', key: 'en' as LanguageISO6391, default: true },
{ label: 'es', key: 'es' as LanguageISO6391 },
{ label: 'pt', key: 'pt' as LanguageISO6391 },
],
features: {
automaticTranslation: {
Expand Down Expand Up @@ -58,9 +61,13 @@ beforeEach(async () => {
serviceName: RequestEntityTranslation.SERVICE_NAME,
});
jest.spyOn(taskManager, 'startTask').mockImplementation(async () => '');

const transactionManager = DefaultTransactionManager();

requestEntityTranslation = new RequestEntityTranslation(
taskManager,
AutomaticTranslationFactory.defaultATConfigDataSource(DefaultTransactionManager()),
AutomaticTranslationFactory.defaultATConfigDataSource(transactionManager),
DefaultEntitiesDataSource(transactionManager),
new Validator<EntityInputModel>(entityInputDataSchema),
mockLogger
);
Expand All @@ -71,27 +78,55 @@ afterAll(async () => {
});

describe('RequestEntityTranslation', () => {
it('should send a task in the automatic translation service queue', async () => {
const languageFromEntity = fixtures.entities?.find(e => e.language === 'en') as EntitySchema;
languageFromEntity._id = languageFromEntity?._id?.toString();
languageFromEntity.template = languageFromEntity?.template?.toString();

await requestEntityTranslation.execute(languageFromEntity!);

expect(taskManager.startTask).toHaveBeenCalledTimes(2);
describe('on requests that should be processed', () => {
beforeEach(async () => {
const languageFromEntity = {
...fixtures.entities?.find(e => e.language === 'en'),
} as EntitySchema;
languageFromEntity._id = languageFromEntity?._id?.toString();
languageFromEntity.template = languageFromEntity?.template?.toString();

await requestEntityTranslation.execute(languageFromEntity!);
});

expect(taskManager.startTask).toHaveBeenCalledWith({
key: ['tenant', 'entity1', factory.commonPropertiesTitleId('template1').toString()],
text: 'entity1',
language_from: 'en',
languages_to: ['es'],
it('should call save entities with pending translation', async () => {
const entities =
(await testingDB.mongodb?.collection('entities').find({ sharedId: 'entity1' }).toArray()) ||
[];
expect(entities.find(e => e.language === 'es')).toMatchObject({
title: `${RequestEntityTranslation.AITranslationPendingText} entity1`,
metadata: {
text1: [{ value: `${RequestEntityTranslation.AITranslationPendingText} original text1` }],
},
});
expect(entities.find(e => e.language === 'pt')).toMatchObject({
title: `${RequestEntityTranslation.AITranslationPendingText} entity1`,
metadata: {
text1: [{ value: `${RequestEntityTranslation.AITranslationPendingText} original text1` }],
},
});
expect(entities.find(e => e.language === 'en')).toMatchObject({
title: 'entity1',
metadata: { text1: [{ value: 'original text1' }] },
});
});

expect(taskManager.startTask).toHaveBeenCalledWith({
key: ['tenant', 'entity1', factory.property('text1')._id?.toString()],
text: 'original text1',
language_from: 'en',
languages_to: ['es'],
it('should send a task to the automatic translation service queue', () => {
expect(taskManager.startTask).toHaveBeenCalledTimes(2);

expect(taskManager.startTask).toHaveBeenCalledWith({
key: ['tenant', 'entity1', factory.commonPropertiesTitleId('template1').toString()],
text: 'entity1',
language_from: 'en',
languages_to: ['es', 'pt'],
});

expect(taskManager.startTask).toHaveBeenCalledWith({
key: ['tenant', 'entity1', factory.property('text1')._id?.toString()],
text: 'original text1',
language_from: 'en',
languages_to: ['es', 'pt'],
});
});
});

Expand All @@ -100,7 +135,7 @@ describe('RequestEntityTranslation', () => {
'entity2',
'template1',
{},
{ language: 'pt' }
{ language: 'kg' }
);
entityWithNotSupportedLanguage._id = entityWithNotSupportedLanguage?._id?.toString();
entityWithNotSupportedLanguage.template = entityWithNotSupportedLanguage?.template?.toString();
Expand All @@ -116,16 +151,6 @@ describe('RequestEntityTranslation', () => {
);
});

it('should call Logger.info two times', async () => {
const languageFromEntity = fixtures.entities?.find(e => e.language === 'en') as EntitySchema;
languageFromEntity._id = languageFromEntity?._id?.toString();
languageFromEntity.template = languageFromEntity?.template?.toString();

await requestEntityTranslation.execute(languageFromEntity!);

expect(mockLogger.info).toHaveBeenCalledTimes(2);
});

it('should NOT send any task if there is no other language to translate', async () => {
await testingEnvironment.setFixtures({
...fixtures,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,20 @@ import { Logger } from 'api/log.v2/contracts/Logger';
import { SaveEntityTranslations } from '../SaveEntityTranslations';
import { TranslationResult, translationResultSchema } from '../types/TranslationResult';
import { ValidationError, Validator } from '../infrastructure/Validator';
import { saveEntityFixtures } from './fixtures/SaveEntity.fixtures';

const factory = getFixturesFactory();

beforeEach(async () => {
const fixtures = {
templates: [
factory.template('template1', [
{
_id: factory.id('propertyName'),
name: 'propertyName',
type: 'text',
label: 'Prop 1',
},
]),
],
entities: [
...factory.entityInMultipleLanguages(['en', 'pt', 'es'], 'entity', 'template1', {
propertyName: [{ value: 'original text' }],
}),
],
settings: [
{
languages: [
{ label: 'en', key: 'en' as LanguageISO6391, default: true },
{ label: 'pt', key: 'pt' as LanguageISO6391 },
{ label: 'es', key: 'es' as LanguageISO6391 },
],
},
],
};
const fixtures = saveEntityFixtures(factory);
await testingEnvironment.setUp(fixtures);
});

afterAll(async () => {
await testingEnvironment.tearDown();
});

describe('GenerateAutomaticTranslationConfig', () => {
describe('SaveEntityTranslations', () => {
let saveEntityTranslations: SaveEntityTranslations;
let mockLogger: Logger;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getFixturesFactory } from 'api/utils/fixturesFactory';
import { LanguageISO6391 } from 'shared/types/commonTypes';

export const saveEntityFixtures = (factory: ReturnType<typeof getFixturesFactory>) => ({
templates: [
factory.template('template1', [
{
_id: factory.id('propertyName'),
name: 'propertyName',
type: 'text',
label: 'Prop 1',
},
]),
],
entities: [
...factory.entityInMultipleLanguages(['en', 'pt', 'es'], 'entity', 'template1', {
propertyName: [{ value: 'original text' }],
}),
...factory.entityInMultipleLanguages(
['en', 'pt', 'es'],
'entity_with_wrong_template',
'wrong_template',
{
propertyName: [{ value: 'original text' }],
}
),
],
settings: [
{
languages: [
{ label: 'en', key: 'en' as LanguageISO6391, default: true },
{ label: 'pt', key: 'pt' as LanguageISO6391 },
{ label: 'es', key: 'es' as LanguageISO6391 },
],
},
],
});
1 change: 1 addition & 0 deletions app/api/utils/fixturesFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ function getFixturesFactory() {
};
},

// eslint-disable-next-line max-params
entityInMultipleLanguages(
languages: string[],
id: string,
Expand Down

0 comments on commit 2b09bac

Please sign in to comment.