languages fixes

This commit is contained in:
CITE\spapacharalampous 2024-09-10 17:05:47 +03:00
parent 1f9aaf616e
commit 2d7c3d0f24
9 changed files with 106 additions and 54 deletions

View File

@ -33,6 +33,8 @@ public class LanguageQuery extends QueryBase<LanguageEntity> {
private Collection<String> codes;
private Collection<UUID> excludedIds;
private Collection<UUID> tenantIds;
private Boolean tenantIsSet;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -101,6 +103,31 @@ public class LanguageQuery extends QueryBase<LanguageEntity> {
return this;
}
public LanguageQuery clearTenantIds() {
this.tenantIds = null;
return this;
}
public LanguageQuery tenantIds(UUID value) {
this.tenantIds = List.of(value);
return this;
}
public LanguageQuery tenantIds(UUID... value) {
this.tenantIds = Arrays.asList(value);
return this;
}
public LanguageQuery tenantIds(Collection<UUID> values) {
this.tenantIds = values;
return this;
}
public LanguageQuery tenantIsSet(Boolean value) {
this.tenantIsSet = value;
return this;
}
public LanguageQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
@ -136,7 +163,7 @@ public class LanguageQuery extends QueryBase<LanguageEntity> {
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.isActives) || this.isEmpty(this.excludedIds) || this.isEmpty(this.codes);
return this.isEmpty(this.ids) || this.isEmpty(this.isActives) || this.isEmpty(this.excludedIds) || this.isEmpty(this.codes) || this.isEmpty(this.tenantIds);
}
@Override
@ -169,6 +196,16 @@ public class LanguageQuery extends QueryBase<LanguageEntity> {
notInClause.value(item);
predicates.add(notInClause.not());
}
if (this.tenantIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(LanguageEntity._tenantId));
for (UUID item : this.tenantIds)
inClause.value(item);
predicates.add(inClause);
}
if (this.tenantIsSet != null) {
if (this.tenantIsSet) predicates.add(queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(LanguageEntity._tenantId)));
else predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(LanguageEntity._tenantId)));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);

View File

@ -3,7 +3,6 @@ package org.opencdmp.service.language;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
@ -23,8 +22,6 @@ import org.opencdmp.model.Language;
import org.opencdmp.model.builder.LanguageBuilder;
import org.opencdmp.model.deleter.LanguageDeleter;
import org.opencdmp.model.persist.LanguagePersist;
import org.opencdmp.query.LanguageQuery;
import org.opencdmp.query.TenantQuery;
import org.opencdmp.service.planblueprint.PlanBlueprintServiceImpl;
import org.opencdmp.service.storage.StorageFileService;
import org.slf4j.LoggerFactory;
@ -47,7 +44,6 @@ public class LanguageServiceImpl implements LanguageService {
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final QueryFactory queryFactory;
private final ConventionService conventionService;
private final MessageSource messageSource;
private final ErrorThesaurusProperties errors;
@ -55,13 +51,12 @@ public class LanguageServiceImpl implements LanguageService {
public LanguageServiceImpl(
TenantEntityManager entityManager, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory, QueryFactory queryFactory,
TenantEntityManager entityManager, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory,
ConventionService conventionService, MessageSource messageSource, ErrorThesaurusProperties errors, StorageFileService storageFileService){
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.queryFactory = queryFactory;
this.conventionService = conventionService;
this.messageSource = messageSource;
this.errors = errors;
@ -98,16 +93,6 @@ public class LanguageServiceImpl implements LanguageService {
this.entityManager.flush();
if (!isUpdate) {
Long languagesWithThisCode = this.queryFactory.query(LanguageQuery.class).disableTracking()
.isActive(IsActive.Active)
.excludedIds(data.getId())
.codes(data.getCode())
.count();
if (languagesWithThisCode > 0) throw new MyValidationException(this.errors.getTenantCodeExists().getCode(), this.errors.getTenantCodeExists().getMessage());
}
return this.builderFactory.builder(LanguageBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(BaseFieldSet.build(fields, Language._id), data);
}

View File

@ -9,6 +9,7 @@ import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
@ -17,13 +18,17 @@ import jakarta.transaction.Transactional;
import jakarta.xml.bind.JAXBException;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.data.LanguageEntity;
import org.opencdmp.data.TenantEntity;
import org.opencdmp.model.Language;
import org.opencdmp.model.Tenant;
import org.opencdmp.model.builder.LanguageBuilder;
import org.opencdmp.model.censorship.LanguageCensor;
import org.opencdmp.model.persist.LanguagePersist;
import org.opencdmp.model.result.QueryResult;
import org.opencdmp.query.LanguageQuery;
import org.opencdmp.query.TenantQuery;
import org.opencdmp.query.lookup.LanguageLookup;
import org.opencdmp.service.language.LanguageService;
import org.slf4j.LoggerFactory;
@ -108,14 +113,30 @@ public class LanguageController {
return model;
}
@GetMapping("public/code/{code}")
public Language get(@PathVariable("code") String code, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException {
@GetMapping(value = {"public/code/{code}/{tenantCode}", "public/code/{code}"})
public Language get(@PathVariable("code") String code, @PathVariable(value = "tenantCode", required = false) String tenantCode, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException {
logger.debug(new MapLogEntry("retrieving" + Language.class.getSimpleName()).And("code", code).And("fields", fieldSet));
this.censorFactory.censor(LanguageCensor.class).censor(fieldSet, null);
LanguageQuery query = this.queryFactory.query(LanguageQuery.class).disableTracking().authorize(EnumSet.of(Public)).codes(code);
Language model = this.builderFactory.builder(LanguageBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet));
LanguageQuery query = this.queryFactory.query(LanguageQuery.class).disableTracking().authorize(EnumSet.of(Public)).codes(code).tenantIsSet(false);
Language model = null;
if (tenantCode != null && !tenantCode.isEmpty()) {
TenantEntity tenant = this.queryFactory.query(TenantQuery.class).codes(tenantCode).firstAs(new BaseFieldSet(Tenant._id));
if (tenant == null)
throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{tenantCode, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
query.tenantIds(tenant.getId()).tenantIsSet(true);
model = this.builderFactory.builder(LanguageBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet));
if (model == null) {
query.clearTenantIds().tenantIsSet(false);
model = this.builderFactory.builder(LanguageBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet));
}
}
else {
model = this.builderFactory.builder(LanguageBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet));
}
if (model == null)
throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{code, Language.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -131,20 +152,34 @@ public class LanguageController {
return model;
}
@PostMapping("public/available-languages")
public QueryResult<String> queryLanguageCodes(@RequestBody LanguageLookup lookup) {
@PostMapping(value = {"public/available-languages/{tenantCode}", "public/available-languages"})
public QueryResult<String> queryLanguageCodes(@RequestBody LanguageLookup lookup, @PathVariable(value = "tenantCode", required = false) String tenantCode) {
logger.debug("querying {}", Language.class.getSimpleName());
this.censorFactory.censor(LanguageCensor.class).censor(lookup.getProject(), null);
LanguageQuery query = lookup.enrich(this.queryFactory).authorize(EnumSet.of(Public));
query.setOrder(new Ordering().addAscending(Language._ordinal));
List<LanguageEntity> data = query.collectAs(lookup.getProject());
List<Language> models = this.builderFactory.builder(LanguageBuilder.class).authorize(EnumSet.of(Public)).build(lookup.getProject(), data);
query.tenantIsSet(false);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
List<LanguageEntity> data = query.collectAs(new BaseFieldSet(Language._code));
if (tenantCode != null && !tenantCode.isBlank()) {
TenantEntity tenant = this.queryFactory.query(TenantQuery.class).codes(tenantCode).firstAs(new BaseFieldSet(Tenant._id));
if (tenant == null)
throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{tenantCode, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
return new QueryResult<>(models.stream().map(Language::getCode).collect(Collectors.toList()), count);
query.tenantIsSet(true).tenantIds(tenant.getId());
List<LanguageEntity> tenantLanguages = query.collectAs(new BaseFieldSet(Language._code));
for (LanguageEntity l : tenantLanguages) {
if (data.stream().noneMatch(d1 -> d1.getCode().equals(l.getCode()))) {
data.add(l);
}
}
}
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : data.size();
return new QueryResult<>(data.stream().map(LanguageEntity::getCode).distinct().collect(Collectors.toList()), count);
}
@PostMapping("persist")

View File

@ -1,6 +1,6 @@
import { OverlayModule } from '@angular/cdk/overlay';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { APP_INITIALIZER, LOCALE_ID, NgModule } from '@angular/core';
import { APP_INITIALIZER, Injector, LOCALE_ID, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MAT_MOMENT_DATE_FORMATS, MatMomentDateModule } from '@angular/material-moment-adapter';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
@ -46,8 +46,8 @@ import { OpenCDMPCustomTranslationCompiler } from './utilities/translate/opencdm
import { Router } from '@angular/router';
// AoT requires an exported function for factories
export function HttpLoaderFactory(languageHttpService: LanguageHttpService) {
return new TranslateServerLoader(languageHttpService);
export function HttpLoaderFactory(languageHttpService: LanguageHttpService, authService: AuthService) {
return new TranslateServerLoader(languageHttpService, authService);
}
const cookieConfig: NgcCookieConsentConfig = {
@ -138,7 +138,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [LanguageHttpService]
deps: [LanguageHttpService, AuthService]
}
}),
OverlayModule,

View File

@ -42,7 +42,7 @@ export class AuthService extends BaseService {
private _selectedTenant: string;
constructor(
private installationConfiguration: ConfigurationService,
private language: TranslateService,
// private language: TranslateService,
private router: Router,
private zone: NgZone,
private keycloakService: KeycloakService,
@ -361,22 +361,13 @@ export class AuthService extends BaseService {
onAuthenticateSuccess(returnUrl: string): void {
this.authState(true);
this.uiNotificationService.snackBarNotification(
this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'),
SnackBarNotificationLevel.Success
);
// this.uiNotificationService.snackBarNotification(
// this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'),
// SnackBarNotificationLevel.Success
// );
this.zone.run(() => this.router.navigateByUrl(returnUrl));
}
onAuthenticateSuccessReload(): void {
this.authState(true);
this.uiNotificationService.snackBarNotification(
this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'),
SnackBarNotificationLevel.Success
);
window.location.reload();
}
public hasPermission(permission: AppPermission): boolean {
// if (!this.installationConfiguration.appServiceEnabled) { return true; } //TODO: maybe reconsider
return this.evaluatePermission(this.appAccount?.permissions || [], permission);

View File

@ -38,14 +38,15 @@ export class LanguageHttpService {
catchError((error: any) => throwError(error)));
}
getSingleWithCode(code: string, reqFields: string[] = []): Observable<Language> {
const url = `${this.apiBase}/public/code/${code}`;
getSingleWithCode(code: string, tenantCode: string | null, reqFields: string[] = []): Observable<Language> {
let url = `${this.apiBase}/public/code/${code}`;
if (tenantCode) url += `/${tenantCode}`;
const options: HttpParamsOptions = { fromObject: { f: reqFields } };
let params: BaseHttpParams = new BaseHttpParams(options);
params.interceptorContext = {
excludedInterceptors: [InterceptorType.AuthToken,
InterceptorType.TenantHeaderInterceptor]
]
};
return this.http

View File

@ -4,16 +4,20 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { LanguageHttpService } from './language.http.service';
import { Injector } from '@angular/core';
import { AuthService } from '../auth/auth.service';
export class TranslateServerLoader implements TranslateLoader {
constructor(
private languageHttpService: LanguageHttpService
) {
private languageHttpService: LanguageHttpService,
private authService: AuthService,
) { }
}
getTranslation(lang: string): Observable<any> {
return this.languageHttpService.getSingleWithCode(lang, [
const tenantCode = this.authService.selectedTenant();
return this.languageHttpService.getSingleWithCode(lang, tenantCode == 'default' ? null : tenantCode, [
nameof<Language>(x => x.id),
nameof<Language>(x => x.code),
nameof<Language>(x => x.payload),

View File

@ -199,7 +199,7 @@ export class LanguageEditorComponent extends BaseEditor<LanguageEditorModel, Lan
public overrideFromFile(matCheckBox: MatCheckboxChange, code: string) {
if (matCheckBox.checked == true) {
this.languageHttpService.getSingleWithCode(code, LanguageEditorResolver.lookupFields())
this.languageHttpService.getSingleWithCode(code, this.authService.selectedTenant(), LanguageEditorResolver.lookupFields())
.pipe(takeUntil(this._destroyed))
.subscribe(language => {
this.formGroup.get('payload').patchValue(language.payload);

View File

@ -5,7 +5,6 @@ import { CultureService } from '@app/core/services/culture/culture-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseService } from '@common/base/base.service';
import { TranslateService } from '@ngx-translate/core';
import { TranslateServerLoader } from '@app/core/services/language/server.loader';
import { LanguageService } from '@app/core/services/language/language.service';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';