Skip to content

Commit

Permalink
Merge pull request #360 from ever-co/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
evereq committed Jan 25, 2023
2 parents 49458aa + 590ad19 commit 389b387
Show file tree
Hide file tree
Showing 32 changed files with 1,695 additions and 525 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ All other brand and product names are trademarks, registered trademarks or servi
[![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
[![Circle CI](https://circleci.com/gh/ever-co/ever-traduora.svg?style=svg)](https://circleci.com/gh/ever-co/ever-traduora)
[![codecov](https://codecov.io/gh/ever-co/ever-traduora/branch/master/graph/badge.svg)](https://codecov.io/gh/ever-co/ever-traduora)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/8e0f1c13c3d94f18b1523b896d4500aa)](https://www.codacy.com/manual/Ever/ever-traduora?utm_source=github.com&utm_medium=referral&utm_content=ever-co/ever-traduora&utm_campaign=Badge_Grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/0d5e1c68dc1e44c79249241b4abb15b8)](https://www.codacy.com/gh/ever-co/ever-traduora/dashboard?utm_source=github.com&utm_medium=referral&utm_content=ever-co/ever-traduora&utm_campaign=Badge_Grade)
[![DeepScan grade](https://deepscan.io/api/teams/3293/projects/16761/branches/365112/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=3293&pid=16761&bid=365112)
[![Known Vulnerabilities](https://snyk.io/test/github/ever-co/ever-traduora/badge.svg)](https://snyk.io/test/github/ever-co/ever-traduora)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/ever-co/ever-traduora.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/ever-co/ever-traduora/alerts/)
Expand Down
9 changes: 5 additions & 4 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ever-traduora-api",
"version": "0.19.3",
"version": "0.19.4",
"license": "AGPL-3.0-only",
"homepage": "https://traduora.co",
"repository": {
Expand Down Expand Up @@ -42,13 +42,14 @@
"class-validator": "^0.13.1",
"csv": "^5.5.3",
"csv-stringify": "^6.0.5",
"ejs": "^3.1.6",
"ejs": "^3.1.7",
"generate-password": "^1.6.1",
"gettext-parser": "^4.0.4",
"handlebars": "^4.7.7",
"iconv-lite": "^0.6.3",
"js-yaml": "^4.1.0",
"moment": "^2.29.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"morgan": "^1.10.0",
"mysql": "^2.18.1",
"node-native2ascii": "^0.2.0",
Expand Down Expand Up @@ -88,7 +89,7 @@
"nodemon": "^2.0.7",
"prettier": "^2.4.0",
"rimraf": "^3.0.2",
"semver-regex": "^4.0.0",
"semver-regex": "^4.0.3",
"supertest": "^6.2.2",
"ts-jest": "^27.0.5",
"ts-loader": "^9.2.5",
Expand Down
2 changes: 1 addition & 1 deletion api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const config = {
},
db: {
default: {
type: 'mysql',
type: env.TR_DB_TYPE || 'mysql',
host: env.TR_DB_HOST || '127.0.0.1',
port: parseInt(env.TR_DB_PORT, 10) || 3306,
username: env.TR_DB_USER || 'root',
Expand Down
51 changes: 46 additions & 5 deletions api/src/controllers/exports.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { phpExporter } from '../formatters/php';
import { ApiOAuth2, ApiTags, ApiOperation, ApiProduces, ApiResponse } from '@nestjs/swagger';
import { androidXmlExporter } from '../formatters/android-xml';
import { resXExporter } from '../formatters/resx';
import { merge } from 'lodash';

@Controller('api/v1/projects/:projectId/exports')
export class ExportsController {
Expand Down Expand Up @@ -73,15 +74,55 @@ export class ExportsController {
.orderBy('term.value', 'ASC')
.getMany();

let termsWithTranslationsMapped = termsWithTranslations.map(t => ({
term: t.value,
translation: t.translations.length === 1 ? t.translations[0].value : '',
}));

if (query.fallbackLocale) {
termsWithTranslationsMapped = termsWithTranslationsMapped.filter(t => t.translation !== '');
}

const data: IntermediateTranslationFormat = {
iso: query.locale,
translations: termsWithTranslations.map(t => ({
term: t.value,
translation: t.translations.length === 1 ? t.translations[0].value : '',
})),
translations: termsWithTranslationsMapped,
};

const serialized = await this.dump(query.format, data);
let serialized = await this.dump(query.format, data);

if (query.fallbackLocale) {
const fallbackProjectLocale = await this.projectLocaleRepo.findOne({
where: {
project: membership.project,
locale: {
code: query.fallbackLocale,
},
},
});

if (fallbackProjectLocale) {
const fallbackTermsWithTranslations = await this.termRepo
.createQueryBuilder('term')
.leftJoinAndSelect('term.translations', 'translation', 'translation.projectLocaleId = :projectLocaleId', {
projectLocaleId: fallbackProjectLocale.id,
})
.where('term.projectId = :projectId', { projectId })
.orderBy('term.value', 'ASC')
.getMany();

const fallbackTermsWithTranslationsMapped = fallbackTermsWithTranslations.map(t => ({
term: t.value,
translation: t.translations.length === 1 ? t.translations[0].value : '',
}));

const dataWithFallback: IntermediateTranslationFormat = {
iso: query.locale,
translations: merge(fallbackTermsWithTranslationsMapped, data.translations),
};

serialized = await this.dump(query.format, dataWithFallback);
}
}

res.status(HttpStatus.OK);
res.contentType('application/octet-stream');
Expand Down
7 changes: 6 additions & 1 deletion api/src/controllers/term.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default class TermController {
const data = terms.map(t => ({
id: t.id,
value: t.value,
context: t.context,
labels: t.labels,
date: t.date,
}));
Expand All @@ -65,6 +66,7 @@ export default class TermController {

const term = this.termRepo.create({
value: payload.value,
context: payload.context,
project: membership.project,
});

Expand Down Expand Up @@ -94,6 +96,7 @@ export default class TermController {
data: {
id: term.id,
value: term.value,
context: term.context,
labels: [],
date: term.date,
},
Expand All @@ -108,19 +111,21 @@ export default class TermController {
@ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized' })
async update(@Req() req, @Param('projectId') projectId: string, @Param('termId') termId: string, @Body() payload: UpdateTermRequest) {
const user = this.auth.getRequestUserOrClient(req);

await this.auth.authorizeProjectAction(user, projectId, ProjectAction.EditTerm);

// Ensure is project term
await this.termRepo.findOneOrFail({ where: { id: termId, project: { id: projectId } } });

await this.termRepo.update({ id: termId }, { value: payload.value });
await this.termRepo.update({ id: termId }, { value: payload.value, context: payload.context });

const term = await this.termRepo.findOneOrFail({ id: termId }, { relations: ['labels'] });

return {
data: {
id: term.id,
value: term.value,
context: term.context,
labels: term.labels,
date: term.date,
},
Expand Down
19 changes: 19 additions & 0 deletions api/src/domain/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export class ProjectTermDTO {
id: string;
@ApiProperty()
value: string;
@ApiProperty({
nullable: true,
})
context: string | null;
@ApiProperty()
labels: ProjectLabelDTO[];
@ApiProperty()
Expand Down Expand Up @@ -558,13 +562,23 @@ export class AddTermRequest {
@Length(1, 255)
@Validate(IsNotOnlyWhitespace)
value: string;

@ApiPropertyOptional({ minLength: 0, maxLength: 1000 })
@IsOptional()
@Length(0, 1000)
context: string | undefined;
}

export class UpdateTermRequest {
@ApiProperty({ minLength: 1, maxLength: 255 })
@Length(1, 255)
@Validate(IsNotOnlyWhitespace)
value: string;

@ApiPropertyOptional({ minLength: 0, maxLength: 1000 })
@IsOptional()
@Length(0, 1000)
context: string | undefined;
}

export class AddLocaleRequest {
Expand Down Expand Up @@ -609,6 +623,11 @@ export class ExportQuery {
@ApiProperty({ minLength: 2, maxLength: 16 })
locale: string;

@Length(2, 16)
@ApiProperty({ minLength: 2, maxLength: 16 })
@IsOptional()
fallbackLocale: string;

@IsEnum(ImportExportFormat)
@ApiProperty({ enum: ImportExportFormat })
format: ImportExportFormat;
Expand Down
5 changes: 5 additions & 0 deletions api/src/entity/term.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export class Term {
@Column()
value: string;

@Column({
nullable: true,
})
context: string | null;

@ManyToOne(() => Project, { onDelete: 'CASCADE', nullable: false })
@JoinColumn()
project: Project;
Expand Down
11 changes: 11 additions & 0 deletions api/src/migrations/1667573768424-add-term-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class addTermContext1667573768424 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `term` ADD COLUMN `context` TEXT DEFAULT NULL AFTER `value`');
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `term` DROP COLUMN `context`');
}
}
29 changes: 25 additions & 4 deletions api/test/term.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,23 @@ describe('TermController (e2e)', () => {
})
.expect(201)
.expect(res => {
expect(res.body.data).toHaveExactProperties(['id', 'value', 'labels', 'date']);
expect(res.body.data).toHaveExactProperties(['id', 'value', 'context', 'labels', 'date']);
expect(res.body.data.labels).toEqual([]);
});
});

it('/api/v1/projects/:projectId/terms (POST) should create terms with context for project', async () => {
await request(app.getHttpServer())
.post(`/api/v1/projects/${testProject.id}/terms`)
.set('Authorization', `Bearer ${testingUser.accessToken}`)
.send({
value: 'term.one',
context: 'Context one',
})
.expect(201)
.expect(res => {
expect(res.body.data).toHaveExactProperties(['id', 'value', 'context', 'labels', 'date']);
expect(res.body.data.context).toEqual('Context one');
expect(res.body.data.labels).toEqual([]);
});
});
Expand All @@ -48,7 +64,7 @@ describe('TermController (e2e)', () => {
.expect(200)
.expect(res => {
expect(res.body.data).toHaveLength(1);
expect(res.body.data[0]).toHaveExactProperties(['id', 'value', 'labels', 'date']);
expect(res.body.data[0]).toHaveExactProperties(['id', 'value', 'context', 'labels', 'date']);
});
});

Expand Down Expand Up @@ -84,6 +100,7 @@ describe('TermController (e2e)', () => {
.set('Authorization', `Bearer ${testingUser.accessToken}`)
.send({
value: 'term.one',
context: 'Context one',
})
.expect(201)
.expect(res => {
Expand All @@ -95,12 +112,14 @@ describe('TermController (e2e)', () => {
.set('Authorization', `Bearer ${testingUser.accessToken}`)
.send({
value: 'term.two',
context: 'Context two',
})
.expect(200)
.expect(res => {
expect(res.body.data).toHaveExactProperties(['id', 'value', 'labels', 'date']);
expect(res.body.data).toHaveExactProperties(['id', 'value', 'context', 'labels', 'date']);
expect(res.body.data.id).toEqual(termId);
expect(res.body.data.value).toEqual('term.two');
expect(res.body.data.context).toEqual('Context two');
});
});

Expand All @@ -123,12 +142,14 @@ describe('TermController (e2e)', () => {
.set('Authorization', `Bearer ${testingUser.accessToken}`)
.send({
value: 'term.two őúüöá 😀👍🍉你好',
context: 'Context őúüöá 😀👍🍉你好',
})
.expect(200)
.expect(res => {
expect(res.body.data).toHaveExactProperties(['id', 'value', 'labels', 'date']);
expect(res.body.data).toHaveExactProperties(['id', 'value', 'context', 'labels', 'date']);
expect(res.body.data.id).toEqual(termId);
expect(res.body.data.value).toEqual('term.two őúüöá 😀👍🍉你好');
expect(res.body.data.context).toEqual('Context őúüöá 😀👍🍉你好');
});
});

Expand Down
Loading

1 comment on commit 389b387

@vercel
Copy link

@vercel vercel bot commented on 389b387 Jan 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

traduora-docs-co – ./

traduora-docs-co-ever-co.vercel.app
docs.traduora.co
traduora-docs-co-git-master-ever-co.vercel.app

Please sign in to comment.