Skip to content

Commit e9abfcb

Browse files
committed
feat: implement glossary view
1 parent 03595cf commit e9abfcb

File tree

27 files changed

+1063
-341
lines changed

27 files changed

+1063
-341
lines changed

backend/data/src/main/kotlin/io/tolgee/model/glossary/GlossaryTerm.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import jakarta.persistence.*
88
@Entity
99
@ActivityLoggedEntity
1010
class GlossaryTerm(
11+
@Column(columnDefinition = "text")
1112
@ActivityLoggedProp
1213
var description: String? = null,
1314
) : StandardAuditModel() {

backend/data/src/main/kotlin/io/tolgee/model/glossary/GlossaryTermTranslation.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@ package io.tolgee.model.glossary
33
import io.tolgee.activity.annotation.ActivityLoggedEntity
44
import io.tolgee.activity.annotation.ActivityLoggedProp
55
import io.tolgee.model.StandardAuditModel
6+
import jakarta.persistence.Column
67
import jakarta.persistence.Entity
78
import jakarta.persistence.ManyToOne
9+
import jakarta.persistence.Table
10+
import jakarta.persistence.UniqueConstraint
811

912
@Entity
1013
@ActivityLoggedEntity
14+
@Table(
15+
uniqueConstraints = [
16+
UniqueConstraint(columnNames = ["term_id", "languageCode"]),
17+
],
18+
)
1119
class GlossaryTermTranslation(
1220
var languageCode: String,
21+
@Column(columnDefinition = "text")
1322
@ActivityLoggedProp
1423
var text: String? = null,
1524
) : StandardAuditModel() {
1625
@ManyToOne
17-
lateinit var term: GlossaryTerm // TODO: rename to term
26+
lateinit var term: GlossaryTerm
1827
}

backend/data/src/main/resources/db/changelog/schema.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4284,4 +4284,7 @@
42844284
<changeSet author="anty (generated)" id="1743523453022-9">
42854285
<addForeignKeyConstraint baseColumnNames="organization_owner_id" baseTableName="glossary" constraintName="FKtiuusr7urs46e15gjy94jhdfm" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="organization" validate="true"/>
42864286
</changeSet>
4287+
<changeSet author="anty (generated)" id="1744285181273-1">
4288+
<addUniqueConstraint columnNames="term_id, language_code" constraintName="UKg4vsd2n9oxiunjyqj47nfj03b" tableName="glossary_term_translation"/>
4289+
</changeSet>
42874290
</databaseChangeLog>

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/glossary/GlossaryTermController.kt

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermModel
99
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermWithTranslationsModel
1010
import io.tolgee.ee.data.glossary.CreateGlossaryTermRequest
1111
import io.tolgee.ee.data.glossary.CreateGlossaryTermResponse
12-
import io.tolgee.ee.data.glossary.GlossaryTermWithTranslationsView
1312
import io.tolgee.ee.data.glossary.UpdateGlossaryTermRequest
1413
import io.tolgee.ee.service.glossary.GlossaryTermService
1514
import io.tolgee.model.enums.OrganizationRoleType
@@ -23,15 +22,7 @@ import org.springdoc.core.annotations.ParameterObject
2322
import org.springframework.data.domain.Pageable
2423
import org.springframework.data.web.PagedResourcesAssembler
2524
import org.springframework.hateoas.PagedModel
26-
import org.springframework.web.bind.annotation.DeleteMapping
27-
import org.springframework.web.bind.annotation.GetMapping
28-
import org.springframework.web.bind.annotation.PathVariable
29-
import org.springframework.web.bind.annotation.PostMapping
30-
import org.springframework.web.bind.annotation.PutMapping
31-
import org.springframework.web.bind.annotation.RequestBody
32-
import org.springframework.web.bind.annotation.RequestMapping
33-
import org.springframework.web.bind.annotation.RequestParam
34-
import org.springframework.web.bind.annotation.RestController
25+
import org.springframework.web.bind.annotation.*
3526

3627
@RestController
3728
@RequestMapping("/v2/organizations/{organizationId:[0-9]+}/glossaries/{glossaryId:[0-9]+}")
@@ -42,7 +33,6 @@ class GlossaryTermController(
4233
private val glossaryTermWithTranslationsModelAssembler: GlossaryTermWithTranslationsModelAssembler,
4334
private val glossaryTermTranslationModelAssembler: GlossaryTermTranslationModelAssembler,
4435
private val pagedAssembler: PagedResourcesAssembler<GlossaryTerm>,
45-
private val pagedWithTranslationsAssembler: PagedResourcesAssembler<GlossaryTermWithTranslationsView>,
4636
) {
4737
@PostMapping("/terms")
4838
@Operation(summary = "Create a new glossary term")
@@ -59,7 +49,7 @@ class GlossaryTermController(
5949
val (term, translation) = glossaryTermService.create(organizationId, glossaryId, dto)
6050
return CreateGlossaryTermResponse(
6151
term = glossaryTermModelAssembler.toModel(term),
62-
translation = glossaryTermTranslationModelAssembler.toModel(translation),
52+
translation = translation?.let { glossaryTermTranslationModelAssembler.toModel(translation) },
6353
)
6454
}
6555

@@ -111,8 +101,9 @@ class GlossaryTermController(
111101
@PathVariable glossaryId: Long,
112102
@ParameterObject pageable: Pageable,
113103
@RequestParam("search", required = false) search: String?,
104+
@RequestParam("languageTags", required = false) languageTags: List<String>?,
114105
): PagedModel<GlossaryTermModel> {
115-
val terms = glossaryTermService.findAllPaged(organizationId, glossaryId, pageable, search)
106+
val terms = glossaryTermService.findAllPaged(organizationId, glossaryId, pageable, search, languageTags?.toSet())
116107
return pagedAssembler.toModel(terms, glossaryTermModelAssembler)
117108
}
118109

@@ -128,13 +119,13 @@ class GlossaryTermController(
128119
@RequestParam("languageTags", required = false) languageTags: List<String>?,
129120
): PagedModel<GlossaryTermWithTranslationsModel> {
130121
val terms =
131-
glossaryTermService.findAllPagedWithTranslations(
122+
glossaryTermService.findAllPaged(
132123
organizationId,
133124
glossaryId,
134125
pageable,
135126
search,
136127
languageTags?.toSet(),
137128
)
138-
return pagedWithTranslationsAssembler.toModel(terms, glossaryTermWithTranslationsModelAssembler)
129+
return pagedAssembler.toModel(terms, glossaryTermWithTranslationsModelAssembler)
139130
}
140131
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,44 @@
11
package io.tolgee.ee.api.v2.controllers.glossary
22

3+
import io.swagger.v3.oas.annotations.Operation
34
import io.swagger.v3.oas.annotations.tags.Tag
4-
import org.springframework.web.bind.annotation.RequestMapping
5-
import org.springframework.web.bind.annotation.RestController
5+
import io.tolgee.ee.api.v2.hateoas.assemblers.glossary.GlossaryTermTranslationModelAssembler
6+
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermTranslationModel
7+
import io.tolgee.ee.data.glossary.CreateGlossaryTermTranslationRequest
8+
import io.tolgee.ee.service.glossary.GlossaryTermService
9+
import io.tolgee.ee.service.glossary.GlossaryTermTranslationService
10+
import io.tolgee.model.enums.OrganizationRoleType
11+
import io.tolgee.security.authentication.AllowApiAccess
12+
import io.tolgee.security.authentication.AuthTokenType
13+
import io.tolgee.security.authorization.RequiresOrganizationRole
14+
import org.springframework.web.bind.annotation.*
615

716
@RestController
8-
@RequestMapping("/v2/organizations/{organizationId:[0-9]+}/glossaries/{id:[0-9]+}/terms/{termId:[0-9]+}/translations")
9-
@Tag(name = "Glossary Term")
10-
class GlossaryTermTranslationController
17+
@RequestMapping(
18+
"/v2/organizations/{organizationId:[0-9]+}/glossaries/{glossaryId:[0-9]+}/terms/{termId:[0-9]+}/translations",
19+
)
20+
@Tag(name = "Glossary Term Translations")
21+
class GlossaryTermTranslationController(
22+
private val glossaryTermService: GlossaryTermService,
23+
private val glossaryTermTranslationService: GlossaryTermTranslationService,
24+
private val modelAssembler: GlossaryTermTranslationModelAssembler,
25+
) {
26+
@PostMapping()
27+
@Operation(summary = "Set a new glossary term translation for language")
28+
@AllowApiAccess(AuthTokenType.ONLY_PAT)
29+
@RequiresOrganizationRole(OrganizationRoleType.OWNER) // TODO special role for glossaries
30+
fun update(
31+
@PathVariable
32+
organizationId: Long,
33+
@PathVariable
34+
glossaryId: Long,
35+
@PathVariable
36+
termId: Long,
37+
@RequestBody
38+
dto: CreateGlossaryTermTranslationRequest,
39+
): GlossaryTermTranslationModel? {
40+
val glossaryTerm = glossaryTermService.get(organizationId, glossaryId, termId)
41+
val translation = glossaryTermTranslationService.updateOrCreate(glossaryTerm, dto)
42+
return translation?.let { modelAssembler.toModel(translation) }
43+
}
44+
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/hateoas/assemblers/glossary/GlossaryTermWithTranslationsModelAssembler.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ package io.tolgee.ee.api.v2.hateoas.assemblers.glossary
22

33
import io.tolgee.ee.api.v2.controllers.glossary.GlossaryTermController
44
import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermWithTranslationsModel
5-
import io.tolgee.ee.data.glossary.GlossaryTermWithTranslationsView
5+
import io.tolgee.model.glossary.GlossaryTerm
66
import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport
77
import org.springframework.stereotype.Component
88

99
@Component
1010
class GlossaryTermWithTranslationsModelAssembler(
1111
private val glossaryTermTranslationModelAssembler: GlossaryTermTranslationModelAssembler,
12-
) : RepresentationModelAssemblerSupport<GlossaryTermWithTranslationsView, GlossaryTermWithTranslationsModel>(
12+
) : RepresentationModelAssemblerSupport<GlossaryTerm, GlossaryTermWithTranslationsModel>(
1313
GlossaryTermController::class.java,
1414
GlossaryTermWithTranslationsModel::class.java,
1515
) {
16-
override fun toModel(entity: GlossaryTermWithTranslationsView): GlossaryTermWithTranslationsModel {
16+
override fun toModel(entity: GlossaryTerm): GlossaryTermWithTranslationsModel {
1717
return GlossaryTermWithTranslationsModel(
1818
id = entity.id,
1919
description = entity.description,
2020
flagNonTranslatable = entity.flagNonTranslatable,
2121
flagCaseSensitive = entity.flagCaseSensitive,
2222
flagAbbreviation = entity.flagAbbreviation,
2323
flagForbiddenTerm = entity.flagForbiddenTerm,
24-
translations = entity.translations?.map { glossaryTermTranslationModelAssembler.toModel(it) } ?: emptyList(),
24+
translations = entity.translations.map { glossaryTermTranslationModelAssembler.toModel(it) },
2525
)
2626
}
2727
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/data/glossary/CreateGlossaryTermResponse.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import io.tolgee.ee.api.v2.hateoas.model.glossary.GlossaryTermTranslationModel
55

66
data class CreateGlossaryTermResponse(
77
val term: GlossaryTermModel,
8-
val translation: GlossaryTermTranslationModel,
8+
val translation: GlossaryTermTranslationModel?,
99
)
Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.tolgee.ee.repository.glossary
22

3-
import io.tolgee.ee.data.glossary.GlossaryTermWithTranslationsView
43
import io.tolgee.model.glossary.Glossary
54
import io.tolgee.model.glossary.GlossaryTerm
65
import org.springframework.context.annotation.Lazy
@@ -34,25 +33,33 @@ interface GlossaryTermRepository : JpaRepository<GlossaryTerm, Long> {
3433
@Query(
3534
"""
3635
from GlossaryTerm te
37-
left join GlossaryTermTranslation tr on tr.term.id = te.id and tr.languageCode = te.glossary.baseLanguageCode
36+
left join GlossaryTermTranslation tr on tr.term.id = te.id
37+
and tr.languageCode = te.glossary.baseLanguageCode
38+
and (:languageTags is null or tr.languageCode in :languageTags)
3839
where te.glossary.organizationOwner.id = :organizationId
3940
and te.glossary.organizationOwner.deletedAt is null
4041
and te.glossary.id = :glossaryId
4142
and te.glossary.deletedAt is null
42-
and (:search is null or lower(tr.text) like lower(concat('%', cast(:search as text), '%')))
43+
and (:search is null or
44+
lower(te.description) like lower(concat('%', cast(:search as text), '%')) or
45+
lower(tr.text) like lower(concat('%', cast(:search as text), '%'))
46+
)
4347
""",
4448
)
4549
fun findPaged(
4650
organizationId: Long,
4751
glossaryId: Long,
4852
pageable: Pageable,
4953
search: String?,
54+
languageTags: Set<String>?,
5055
): Page<GlossaryTerm>
5156

5257
@Query(
5358
"""
5459
from GlossaryTerm te
55-
left join GlossaryTermTranslation tr on tr.term.id = te.id and tr.languageCode = te.glossary.baseLanguageCode
60+
left join GlossaryTermTranslation tr on tr.term.id = te.id
61+
and tr.languageCode = te.glossary.baseLanguageCode
62+
and (:languageTags is null or tr.languageCode in :languageTags)
5663
where te.glossary = :glossary
5764
and (
5865
:search is null or
@@ -65,33 +72,58 @@ interface GlossaryTermRepository : JpaRepository<GlossaryTerm, Long> {
6572
glossary: Glossary,
6673
pageable: Pageable,
6774
search: String?,
75+
languageTags: Set<String>?,
6876
): Page<GlossaryTerm>
6977

70-
@Query(
71-
"""
72-
select te.id as id,
73-
te.description as description,
74-
te.flagNonTranslatable as flagNonTranslatable,
75-
te.flagCaseSensitive as flagCaseSensitive,
76-
te.flagAbbreviation as flagAbbreviation,
77-
te.flagForbiddenTerm as flagForbiddenTerm,
78-
tr as translations
79-
from GlossaryTerm te
80-
left join GlossaryTermTranslation tr
81-
on tr.term.id = te.id
82-
and (:languageTags is null or tr.languageCode in :languageTags)
83-
where te.glossary = :glossary
84-
and (
85-
:search is null or
86-
lower(te.description) like lower(concat('%', cast(:search as text), '%')) or
87-
lower(tr.text) like lower(concat('%', cast(:search as text), '%'))
88-
)
89-
""",
90-
)
91-
fun findByGlossaryPagedWithTranslations(
92-
glossary: Glossary,
93-
pageable: Pageable,
94-
search: String?,
95-
languageTags: Set<String>?,
96-
): Page<GlossaryTermWithTranslationsView>
78+
// @Query(
79+
// """
80+
// select te.id as id,
81+
// te.description as description,
82+
// te.flagNonTranslatable as flagNonTranslatable,
83+
// te.flagCaseSensitive as flagCaseSensitive,
84+
// te.flagAbbreviation as flagAbbreviation,
85+
// te.flagForbiddenTerm as flagForbiddenTerm,
86+
// tr as translations
87+
// from GlossaryTerm te
88+
// left join GlossaryTermTranslation tr
89+
// on tr.term.id = te.id
90+
// and (:languageTags is null or tr.languageCode in :languageTags)
91+
// left join GlossaryTermTranslation tr_search
92+
// on tr_search.term.id = te.id
93+
// and (:languageTags is null or tr_search.languageCode in :languageTags)
94+
// where te.glossary = :glossary
95+
// and (
96+
// :search is null or
97+
// lower(te.description) like lower(concat('%', cast(:search as text), '%')) or
98+
// lower(tr_search.text) like lower(concat('%', cast(:search as text), '%'))
99+
// )
100+
// """,
101+
// )
102+
// // @Query(
103+
// // """
104+
// // select te.id as id,
105+
// // te.description as description,
106+
// // te.flagNonTranslatable as flagNonTranslatable,
107+
// // te.flagCaseSensitive as flagCaseSensitive,
108+
// // te.flagAbbreviation as flagAbbreviation,
109+
// // te.flagForbiddenTerm as flagForbiddenTerm,
110+
// // tr as translations
111+
// // from GlossaryTerm te
112+
// // left join GlossaryTermTranslation tr
113+
// // on tr.term.id = te.id
114+
// // and (:languageTags is null or tr.languageCode in :languageTags)
115+
// // where te.glossary = :glossary
116+
// // and (
117+
// // :search is null or
118+
// // lower(te.description) like lower(concat('%', cast(:search as text), '%')) or
119+
// // lower(tr.text) like lower(concat('%', cast(:search as text), '%'))
120+
// // )
121+
// // """,
122+
// // )
123+
// fun findByGlossaryPagedWithTranslations(
124+
// glossary: Glossary,
125+
// pageable: Pageable,
126+
// search: String?,
127+
// languageTags: Set<String>?,
128+
// ): Page<GlossaryTermWithTranslationsView>
97129
}

ee/backend/app/src/main/kotlin/io/tolgee/ee/repository/glossary/GlossaryTermTranslationRepository.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.tolgee.ee.repository.glossary
22

3+
import io.tolgee.model.glossary.GlossaryTerm
34
import io.tolgee.model.glossary.GlossaryTermTranslation
45
import org.springframework.context.annotation.Lazy
56
import org.springframework.data.jpa.repository.JpaRepository
@@ -23,4 +24,14 @@ interface GlossaryTermTranslationRepository : JpaRepository<GlossaryTermTranslat
2324
organizationId: Long,
2425
glossaryId: Long,
2526
): Set<String>
27+
28+
fun findByTermAndLanguageCode(
29+
term: GlossaryTerm,
30+
languageCode: String,
31+
): GlossaryTermTranslation?
32+
33+
fun deleteByTermAndLanguageCode(
34+
term: GlossaryTerm,
35+
languageCode: String,
36+
)
2637
}

0 commit comments

Comments
 (0)