Ng17 migration (#10295)

* Migrate to NG17

* [ci:force] - fixed deps

* [ci:force] - fixed build for testing 1

* Fixed build for all the packages

* [ci:force] - fixing lint

* [ci:force] - Fixed lint

* AAE-26163 Fix infinite loop when authentication error event occured (#10272)

* AAE-26163 Logout user after 3 login attempts failed, avoiding infinite loop when an authentication error occured, like when a user machine clock is significantly out of sync

* AAE-26163 Wait to discovery document to be loaded and user not authenticated to perform a ssoLogin, logout user if login fails after 3 attempts

* AAE-26163 Fix missed id_token_hint invoking logout when a login error occured due to a clock significantly out of sync

* AAE-26163 Add fake observable to unit test

* AAE-26163 Show oauth event logs if showDebugInformation is enabled, remove auth items if access token is not valid

* AAE-26163 Improve tryLogin error message

* AAE-26163 Check if token has expired to fix case when user access the application after the token is expired and with a clock significantly out of sync

* AAE-26163 Test logout when clock is out of sync

* AAE-26163 Create a service to check if local machine time is out of sync

* AAE-26163 Update oauthErrorEvent$ and combinedOAuthErrorsStream$ to return errors

* AAE-26163 Output error within combined oauth error event subscription

* AAE-26163 Fix lint problems

* AAE-26163 Logout user when token refresh error happens for the second time, if the token is not refreshed properly after first refresh error

* AAE-26163 Logout user once an oauth error event occur due to clock out of sync

* AAE-26163 Fix retry login error message if the OAuthErrorEvent doesn t return reason

* AAE-26163 Fix the issue where the logout API call is canceled by the authorize call when login fails due to clock synchronization problems, causing an infinite loop.

* remove console.log

* AAE-26163 Fix retry login error message if the OAuthErrorEvent reason is an empty object

* Cherry picked commit from oidc and run fix lint

* [MIGRATION] - fixed build and lint

* [MIGRATION] - Added injectionContext to avoid error NG0203 for unit tests

* [MIGRATION] - Moving mocha to jest

* [MIGRATION] - Fixing failing migrated tests

* [MIGRATION] - Migrating to Jest - working but some tests fails

* Trying to fix js-api unit tests

* Removing testing lib to sync with develop

* Fixed two excluded unit tests

* Removed unused project parts

* Removed unused project parts

* Reduced tserrors on building storybook

* Fixed sonarqube errors

* Removing temporarily eslint rule from publishing

* [MIGRATION] - Fixed lint

* [MIGRATION] - Fixed type

* [MIGRATION] - Rebased

* [MIGRATION] - Readded removed action

* [MIGRATION] - Checking deps

* [MIGRATION] - updated lock

* [ACS-9052] manage versions close button is too low (#10466)

* [ci:force] - Fixed lint

* [ACS-9052] Fixed close button in version manager position

* [ACS-9052] Reverted unwanted changes

---------

Co-authored-by: VitoAlbano <vito.albano.123@gmail.com>

* [MIGRATION] - fixed storybook builds

* [MIGRATION] - Checking if now eslint is releasable

* [MIGRATION] - Changing the building executor for eslint-rules

* Readded rule for peer deps

* Fixed wrong rule

* [ACS-9075] Fixed incorrect buttons labels color (#10489)

* Update package.json

* Fix ACA pipeline

* [ACS-9084] Fixed incorrect color for notification bell icon (#10513)

* Change dialog label padding

* [AAE-26767] - Fixed lint

* [AAE-26767] - Fixed lint

* updated dependencies

* AAE-30733 Fix incorrect alignment of icons in permission list header

* [MIGRATION] - sync package-lock

* [MIGRATION] - Fixed package on core lib

* [MIGRATION] - Removed unused lock

* Fixed licence

* [MIGRATION] - sync lock file

* [MIGRATION] - fixed lint issues

* [ACS-9271][ACA] Login page input labels are cut if the input is not empty (#10637)

* AAE-31453 Override card-view-textitem readonly color

---------

Co-authored-by: Amedeo Lepore <amedeo.lepore@hyland.com>
Co-authored-by: Ehsan Rezaei <ehsan.rezaei@hyland.com>
Co-authored-by: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com>
Co-authored-by: DominikIwanek <dominik.iwanek@hyland.com>
Co-authored-by: swapnil-verma-gl <92505353+swapnil-verma-gl@users.noreply.github.com>
Co-authored-by: Wojciech Duda <69160975+wojd0@users.noreply.github.com>
Co-authored-by: dominikiwanekhyland <141320833+dominikiwanekhyland@users.noreply.github.com>
This commit is contained in:
Vito Albano
2025-02-12 11:58:57 +00:00
committed by GitHub
parent 2284ede0c7
commit 5d64c7f0ed
317 changed files with 16460 additions and 15654 deletions

View File

@@ -69,10 +69,7 @@ describe('StoragePrefixFactory', () => {
}
};
const prefixFactory = new StoragePrefixFactory(
appConfigService as AppConfigService,
externalPrefixFactory
);
const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService, externalPrefixFactory);
prefixFactory.getPrefix().subscribe((prefix) => {
expect(prefix).toBe('prefix-from-factory');
@@ -95,10 +92,7 @@ describe('StoragePrefixFactory', () => {
}
};
const prefixFactory = new StoragePrefixFactory(
appConfigService as AppConfigService,
externalPrefixFactory
);
const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService, externalPrefixFactory);
prefixFactory.getPrefix().subscribe((prefix) => {
expect(prefix).toBe(appConfigPrefix);

View File

@@ -33,7 +33,8 @@ export class StoragePrefixFactory {
constructor(
private appConfigService: AppConfigService,
@Optional()
@Inject(STORAGE_PREFIX_FACTORY_SERVICE) private storagePrefixFactory?: StoragePrefixFactoryService
@Inject(STORAGE_PREFIX_FACTORY_SERVICE)
private storagePrefixFactory?: StoragePrefixFactoryService
) {}
getPrefix(): Observable<string | undefined> {
@@ -43,9 +44,7 @@ export class StoragePrefixFactory {
return of(prefix);
}
return this.storagePrefixFactory ?
this.storagePrefixFactory.getPrefix() :
of(prefix);
return this.storagePrefixFactory ? this.storagePrefixFactory.getPrefix() : of(prefix);
})
);
}

View File

@@ -34,8 +34,7 @@ export function loadAppConfig(
storageService: StorageService,
adfHttpClient: AdfHttpClient,
storagePrefixFactory: StoragePrefixFactory
) {
) {
const init = () => {
adfHttpClient.disableCsrf = appConfigService.get<boolean>(AppConfigValues.DISABLECSRF, true);
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
@@ -45,4 +44,4 @@ export function loadAppConfig(
});
};
return () => appConfigService.load(init);
};
}

View File

@@ -23,9 +23,10 @@ import { AuthenticationService } from '../services/authentication.service';
import { RedirectAuthService } from '../oidc/redirect-auth.service';
const mockNext: HttpHandler = {
handle: () => new Observable(subscriber => {
subscriber.complete();
})
handle: () =>
new Observable((subscriber) => {
subscriber.complete();
})
};
const mockRequest = (url) => new HttpRequest('GET', url);

View File

@@ -18,8 +18,15 @@
import { throwError as observableThrowError, Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import {
HttpHandler, HttpInterceptor, HttpRequest,
HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpHeaders
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpSentEvent,
HttpHeaderResponse,
HttpProgressEvent,
HttpResponse,
HttpUserEvent,
HttpHeaders
} from '@angular/common/http';
import { catchError, mergeMap } from 'rxjs/operators';
import { AuthenticationService } from '../services/authentication.service';
@@ -30,61 +37,52 @@ export class AuthBearerInterceptor implements HttpInterceptor {
private excludedUrlsRegex: RegExp[];
constructor(private authenticationService: AuthenticationService) { }
constructor(private authenticationService: AuthenticationService) {}
private loadExcludedUrlsRegex() {
const excludedUrls = this.bearerExcludedUrls;
this.excludedUrlsRegex = excludedUrls.map((urlPattern) => new RegExp(`^https?://[^/]+/${urlPattern}`, 'i')) || [];
}
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
if (!this.excludedUrlsRegex) {
this.loadExcludedUrlsRegex();
private loadExcludedUrlsRegex() {
const excludedUrls = this.bearerExcludedUrls;
this.excludedUrlsRegex = excludedUrls.map((urlPattern) => new RegExp(`^https?://[^/]+/${urlPattern}`, 'i')) || [];
}
const requestUrl = req.url;
const shallPass: boolean = this.excludedUrlsRegex.some((regex) => regex.test(requestUrl));
if (shallPass) {
return next.handle(req)
.pipe(
catchError((error) => observableThrowError(error))
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
if (!this.excludedUrlsRegex) {
this.loadExcludedUrlsRegex();
}
const requestUrl = req.url;
const shallPass: boolean = this.excludedUrlsRegex.some((regex) => regex.test(requestUrl));
if (shallPass) {
return next.handle(req).pipe(catchError((error) => observableThrowError(error)));
}
return this.authenticationService.addTokenToHeader(requestUrl, req.headers).pipe(
mergeMap((headersWithBearer) => {
const headerWithContentType = this.appendJsonContentType(headersWithBearer, req.body);
const kcReq = req.clone({ headers: headerWithContentType });
return next.handle(kcReq).pipe(catchError((error) => observableThrowError(error)));
})
);
}
return this.authenticationService.addTokenToHeader(requestUrl, req.headers)
.pipe(
mergeMap((headersWithBearer) => {
const headerWithContentType = this.appendJsonContentType(headersWithBearer, req.body);
const kcReq = req.clone({ headers: headerWithContentType});
return next.handle(kcReq)
.pipe(
catchError((error) => observableThrowError(error))
);
})
);
}
private appendJsonContentType(headers: HttpHeaders, reqBody: any): HttpHeaders {
// prevent adding any content type, to properly handle formData with boundary browser generated value,
// as adding any Content-Type its going to break the upload functionality
private appendJsonContentType(headers: HttpHeaders, reqBody: any): HttpHeaders {
if (headers.get('Content-Type') === 'multipart/form-data' && !(reqBody instanceof FormData)) {
return headers.delete('Content-Type');
}
// prevent adding any content type, to properly handle formData with boundary browser generated value,
// as adding any Content-Type its going to break the upload functionality
if (!headers.get('Content-Type') && !(reqBody instanceof FormData)) {
return headers.set('Content-Type', 'application/json;charset=UTF-8');
}
if (headers.get('Content-Type') === 'multipart/form-data' && !(reqBody instanceof FormData)) {
return headers.delete('Content-Type');
return headers;
}
if (!headers.get('Content-Type') && !(reqBody instanceof FormData)) {
return headers.set('Content-Type', 'application/json;charset=UTF-8');
}
return headers;
}
protected get bearerExcludedUrls(): readonly string[] {
return this._bearerExcludedUrls;
}
}

View File

@@ -38,7 +38,6 @@ export interface TicketEntry {
providedIn: 'root'
})
export class ContentAuth {
onLogin = new ReplaySubject<any>(1);
onLogout = new ReplaySubject<any>(1);
onError = new Subject<any>();
@@ -60,9 +59,7 @@ export class ContentAuth {
return this.appConfigService.get<string>(AppConfigValues.ECMHOST) + '/' + contextRootEcm + '/api/-default-/public/authentication/versions/1';
}
constructor(private appConfigService: AppConfigService,
private adfHttpClient: AdfHttpClient,
private storageService: StorageService) {
constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient, private storageService: StorageService) {
this.appConfigService.onLoad.subscribe(() => {
this.setConfig();
});
@@ -72,7 +69,6 @@ export class ContentAuth {
if (this.storageService.getItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL)) {
this.setTicket(this.storageService.getItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL));
}
}
saveUsername(username: string) {
@@ -148,7 +144,8 @@ export class ContentAuth {
this.adfHttpClient.emit('error');
this.onError.next('error');
reject(error);
});
}
);
});
}
@@ -200,12 +197,12 @@ export class ContentAuth {
createTicket(ticketBodyCreate: TicketBody): Promise<TicketEntry> {
if (ticketBodyCreate === null || ticketBodyCreate === undefined) {
this.onError.next((`Missing param ticketBodyCreate`));
this.onError.next(`Missing param ticketBodyCreate`);
throw new Error(`Missing param ticketBodyCreate`);
}
return this.adfHttpClient.post(this.basePath + '/tickets', {bodyParam: ticketBodyCreate});
return this.adfHttpClient.post(this.basePath + '/tickets', { bodyParam: ticketBodyCreate });
}
async requireAlfTicket(): Promise<void> {
@@ -216,5 +213,4 @@ export class ContentAuth {
deleteTicket(): Promise<any> {
return this.adfHttpClient.delete(this.basePath + '/tickets/-me-');
}
}

View File

@@ -22,12 +22,10 @@ import { AppConfigService, AppConfigValues } from '../../app-config/app-config.s
import { StorageService } from '../../common/services/storage.service';
import { ReplaySubject, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ProcessAuth {
onLogin = new ReplaySubject<any>(1);
onLogout = new ReplaySubject<any>(1);
onError = new Subject<any>();
@@ -38,7 +36,8 @@ export class ProcessAuth {
};
authentications: Authentication = {
basicAuth: {ticket: ''}, type: 'activiti'
basicAuth: { ticket: '' },
type: 'activiti'
};
get basePath(): string {
@@ -46,9 +45,7 @@ export class ProcessAuth {
return this.appConfigService.get<string>(AppConfigValues.BPMHOST) + '/' + contextRootBpm;
}
constructor(private appConfigService: AppConfigService,
private adfHttpClient: AdfHttpClient,
private storageService: StorageService) {
constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient, private storageService: StorageService) {
this.appConfigService.onLoad.subscribe(() => {
this.setConfig();
});
@@ -118,7 +115,8 @@ export class ProcessAuth {
this.onError.next('error');
}
reject(error);
});
}
);
});
return promise;
@@ -147,7 +145,8 @@ export class ProcessAuth {
this.adfHttpClient.emit('error');
this.onError.next('error');
reject(error);
});
}
);
});
}

View File

@@ -48,7 +48,7 @@ export const AuthGuardSsoRoleService: CanActivateFn = (route: ActivatedRouteSnap
}
const hasRole = hasRealmRole && hasClientRole;
if (!hasRole && route?.data && route.data['redirectUrl']) {
if (!hasRole && route?.data?.['redirectUrl']) {
const router = inject(Router);
router.navigate(['/' + route.data['redirectUrl']]);
}

View File

@@ -20,39 +20,53 @@ import { IdentityRoleModel } from '../models/identity-role.model';
import { IdentityJoinGroupRequestModel } from '../interfaces/identity-user.service.interface';
export const mockIdentityGroup1 = {
id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: []
id: 'mock-group-id-1',
name: 'Mock Group 1',
path: '/mock',
subGroups: []
} as IdentityGroupModel;
export const mockIdentityGroup2 = {
id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: []
id: 'mock-group-id-2',
name: 'Mock Group 2',
path: '',
subGroups: []
} as IdentityGroupModel;
export const mockIdentityGroup3 = {
id: 'mock-group-id-3', name: 'Mock Group 3', path: '', subGroups: []
id: 'mock-group-id-3',
name: 'Mock Group 3',
path: '',
subGroups: []
} as IdentityGroupModel;
export const mockIdentityGroup4 = {
id: 'mock-group-id-4', name: 'Mock Group 4', path: '', subGroups: []
id: 'mock-group-id-4',
name: 'Mock Group 4',
path: '',
subGroups: []
} as IdentityGroupModel;
export const mockIdentityGroup5 = {
id: 'mock-group-id-5', name: 'Mock Group 5', path: '', subGroups: []
id: 'mock-group-id-5',
name: 'Mock Group 5',
path: '',
subGroups: []
} as IdentityGroupModel;
export const mockIdentityGroupsCount = { count: 10 } as IdentityGroupCountModel;
export const mockIdentityGroups = [
mockIdentityGroup1, mockIdentityGroup2, mockIdentityGroup3, mockIdentityGroup4, mockIdentityGroup5
];
export const mockIdentityGroups = [mockIdentityGroup1, mockIdentityGroup2, mockIdentityGroup3, mockIdentityGroup4, mockIdentityGroup5];
export const roleMappingMock = [
{ id: 'role-id-1', name: 'role-name-1' }, { id: 'role-id-2', name: 'role-name-2' }
{ id: 'role-id-1', name: 'role-name-1' },
{ id: 'role-id-2', name: 'role-name-2' }
];
export const mockIdentityRoles = [
new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-ADMIN-ROLE'}),
new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-USER-ROLE'}),
new IdentityRoleModel({id: 'mock-role-id', name: 'MOCK-ROLE-1'})
new IdentityRoleModel({ id: 'mock-role-id', name: 'MOCK-ADMIN-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id', name: 'MOCK-USER-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id', name: 'MOCK-ROLE-1' })
];
export const clientRoles: IdentityRoleModel[] = [
@@ -60,14 +74,20 @@ export const clientRoles: IdentityRoleModel[] = [
new IdentityRoleModel({ name: 'MOCK-USER-ROLE' })
];
export const mockJoinGroupRequest: IdentityJoinGroupRequestModel = {userId: 'mock-hser-id', groupId: 'mock-group-id', realm: 'mock-realm-name'};
export const mockJoinGroupRequest: IdentityJoinGroupRequestModel = { userId: 'mock-hser-id', groupId: 'mock-group-id', realm: 'mock-realm-name' };
export const mockGroup1 = {
id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: []
id: 'mock-group-id-1',
name: 'Mock Group 1',
path: '/mock',
subGroups: []
} as IdentityGroupModel;
export const mockGroup2 = {
id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: []
id: 'mock-group-id-2',
name: 'Mock Group 2',
path: '',
subGroups: []
} as IdentityGroupModel;
export const mockGroups = [

View File

@@ -31,7 +31,6 @@ import { IdentityRoleModel } from '../models/identity-role.model';
Injectable({ providedIn: 'root' });
export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
getGroups(): Observable<IdentityGroupModel[]> {
return of(mockIdentityGroups);
}
@@ -81,9 +80,7 @@ export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
return of([]);
}
return of(mockIdentityGroups.filter(group =>
group.name.toUpperCase().includes(searchParams.name.toUpperCase())
));
return of(mockIdentityGroups.filter((group) => group.name.toUpperCase().includes(searchParams.name.toUpperCase())));
}
getGroupRoles(_groupId: string): Observable<IdentityRoleModel[]> {
@@ -91,19 +88,21 @@ export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
}
checkGroupHasRole(groupId: string, roleNames: string[]): Observable<boolean> {
return this.getGroupRoles(groupId).pipe(map((groupRoles) => {
let hasRole = false;
if (groupRoles?.length > 0) {
roleNames.forEach((roleName: string) => {
const role = groupRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}));
return this.getGroupRoles(groupId).pipe(
map((groupRoles) => {
let hasRole = false;
if (groupRoles?.length > 0) {
roleNames.forEach((roleName: string) => {
const role = groupRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
})
);
}
getClientIdByApplicationName(_applicationName: string): Observable<string> {
@@ -119,9 +118,7 @@ export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
}
checkGroupHasClientApp(groupId: string, clientId: string): Observable<boolean> {
return this.getClientRoles(groupId, clientId).pipe(
map((response) => response && response.length > 0)
);
return this.getClientRoles(groupId, clientId).pipe(map((response) => response && response.length > 0));
}
checkGroupHasAnyClientAppRole(groupId: string, clientId: string, roleNames: string[]): Observable<boolean> {

View File

@@ -18,37 +18,61 @@
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityRoleModel } from '../models/identity-role.model';
export const mockIdentityUser1: IdentityUserModel = { id: 'mock-user-id-1', username: 'userName1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com' };
export const mockIdentityUser2: IdentityUserModel = { id: 'mock-user-id-2', username: 'userName2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com'};
export const mockIdentityUser3: IdentityUserModel = { id: 'mock-user-id-3', username: 'userName3', firstName: 'first-name-3', lastName: 'last-name-3', email: 'abcde@xyz.com' };
export const mockIdentityUser4: IdentityUserModel = { id: 'mock-user-id-4', username: 'userName4', firstName: 'first-name-4', lastName: 'last-name-4', email: 'abcde@xyz.com' };
export const mockIdentityUser5: IdentityUserModel = { id: 'mock-user-id-5', username: 'userName5', firstName: 'first-name-5', lastName: 'last-name-5', email: 'abcde@xyz.com' };
export const mockIdentityUser1: IdentityUserModel = {
id: 'mock-user-id-1',
username: 'userName1',
firstName: 'first-name-1',
lastName: 'last-name-1',
email: 'abc@xyz.com'
};
export const mockIdentityUser2: IdentityUserModel = {
id: 'mock-user-id-2',
username: 'userName2',
firstName: 'first-name-2',
lastName: 'last-name-2',
email: 'abcd@xyz.com'
};
export const mockIdentityUser3: IdentityUserModel = {
id: 'mock-user-id-3',
username: 'userName3',
firstName: 'first-name-3',
lastName: 'last-name-3',
email: 'abcde@xyz.com'
};
export const mockIdentityUser4: IdentityUserModel = {
id: 'mock-user-id-4',
username: 'userName4',
firstName: 'first-name-4',
lastName: 'last-name-4',
email: 'abcde@xyz.com'
};
export const mockIdentityUser5: IdentityUserModel = {
id: 'mock-user-id-5',
username: 'userName5',
firstName: 'first-name-5',
lastName: 'last-name-5',
email: 'abcde@xyz.com'
};
export const mockIdentityUsers: IdentityUserModel[] = [
mockIdentityUser1,
mockIdentityUser2,
mockIdentityUser3,
mockIdentityUser4,
mockIdentityUser5
];
export const mockIdentityUsers: IdentityUserModel[] = [mockIdentityUser1, mockIdentityUser2, mockIdentityUser3, mockIdentityUser4, mockIdentityUser5];
export const mockIdentityRole = new IdentityRoleModel({ id: 'id-1', name: 'MOCK-ADMIN-ROLE'});
export const mockIdentityRole = new IdentityRoleModel({ id: 'id-1', name: 'MOCK-ADMIN-ROLE' });
export const mockAvailableRoles = [
new IdentityRoleModel({ id: 'mock-role-id-1', name: 'MOCK-ADMIN-ROLE'}),
new IdentityRoleModel({ id: 'mock-role-id-2', name: 'MOCK-USER-ROLE'}),
new IdentityRoleModel({ id: 'mock-role-id-1', name: 'MOCK-ADMIN-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-2', name: 'MOCK-USER-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-3', name: 'MOCK_MODELER-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-5', name: 'MOCK-ROLE-2'})
new IdentityRoleModel({ id: 'mock-role-id-5', name: 'MOCK-ROLE-2' })
];
export const mockAssignedRoles = [
new IdentityRoleModel({ id: 'mock-role-id-1', name: 'MOCK-ADMIN-ROLE'}),
new IdentityRoleModel({ id: 'mock-role-id-1', name: 'MOCK-ADMIN-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-2', name: 'MOCK_MODELER-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-3', name: 'MOCK-ROLE-1' })
];
export const mockEffectiveRoles = [
new IdentityRoleModel({id: 'mock-role-id-1', name: 'MOCK-ACTIVE-ADMIN-ROLE'}),
new IdentityRoleModel({id: 'mock-role-id-2', name: 'MOCK-ACTIVE-USER-ROLE'}),
new IdentityRoleModel({id: 'mock-role-id-3', name: 'MOCK-ROLE-1'})
new IdentityRoleModel({ id: 'mock-role-id-1', name: 'MOCK-ACTIVE-ADMIN-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-2', name: 'MOCK-ACTIVE-USER-ROLE' }),
new IdentityRoleModel({ id: 'mock-role-id-3', name: 'MOCK-ROLE-1' })
];

View File

@@ -35,7 +35,6 @@ import { mockAssignedRoles, mockAvailableRoles, mockEffectiveRoles, mockIdentity
providedIn: 'root'
})
export class IdentityUserServiceMock implements IdentityUserServiceInterface {
getCurrentUserInfo(): IdentityUserModel {
return mockIdentityUser1;
}
@@ -45,9 +44,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
return of([]);
}
return of(mockIdentityUsers.filter(user =>
user.username.toUpperCase().includes(search.toUpperCase())
));
return of(mockIdentityUsers.filter((user) => user.username.toUpperCase().includes(search.toUpperCase())));
}
findUserByUsername(username: string): Observable<IdentityUserModel[]> {
@@ -55,7 +52,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
return of([]);
}
return of(mockIdentityUsers.filter(user => user.username === username));
return of(mockIdentityUsers.filter((user) => user.username === username));
}
findUserByEmail(email: string): Observable<IdentityUserModel[]> {
@@ -63,7 +60,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
return of([]);
}
return of(mockIdentityUsers.filter(user => user.email === email));
return of(mockIdentityUsers.filter((user) => user.email === email));
}
findUserById(id: string): Observable<any> {
@@ -71,7 +68,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
return of([]);
}
return of(mockIdentityUsers.find(user => user.id === id));
return of(mockIdentityUsers.find((user) => user.id === id));
}
getClientRoles(userId: string, _clientId: string): Observable<any[]> {
@@ -83,9 +80,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
}
checkUserHasClientApp(userId: string, clientId: string): Observable<boolean> {
return this.getClientRoles(userId, clientId).pipe(
map((clientRoles) => clientRoles.length > 0)
);
return this.getClientRoles(userId, clientId).pipe(map((clientRoles) => clientRoles.length > 0));
}
checkUserHasAnyClientAppRole(userId: string, clientId: string, roleNames: string[]): Observable<boolean> {
@@ -112,9 +107,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
}
checkUserHasApplicationAccess(userId: string, applicationName: string): Observable<boolean> {
return this.getClientIdByApplicationName(applicationName).pipe(
switchMap((clientId: string) => this.checkUserHasClientApp(userId, clientId))
);
return this.getClientIdByApplicationName(applicationName).pipe(switchMap((clientId: string) => this.checkUserHasClientApp(userId, clientId)));
}
checkUserHasAnyApplicationRole(userId: string, applicationName: string, roleNames: string[]): Observable<boolean> {
@@ -178,19 +171,21 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
}
checkUserHasRole(userId: string, roleNames: string[]): Observable<boolean> {
return this.getUserRoles(userId).pipe(map((userRoles: IdentityRoleModel[]) => {
let hasRole = false;
if (userRoles && userRoles.length > 0) {
roleNames.forEach((roleName: string) => {
const role = userRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}));
return this.getUserRoles(userId).pipe(
map((userRoles: IdentityRoleModel[]) => {
let hasRole = false;
if (userRoles && userRoles.length > 0) {
roleNames.forEach((roleName: string) => {
const role = userRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
})
);
}
queryUsers(_requestQuery: IdentityUserQueryCloudRequestModel): Observable<IdentityUserQueryResponse> {

View File

@@ -128,6 +128,6 @@ export const deleteGroupMappingApi: any = {
export const applicationDetailsMockApi: any = {
oauth2Auth: {
callCustomApi: () => Promise.resolve([{id: 'mock-app-id', name: 'mock-app-name'}])
callCustomApi: () => Promise.resolve([{ id: 'mock-app-id', name: 'mock-app-name' }])
}
};

View File

@@ -29,5 +29,4 @@ export class RedirectionModel {
this.url = obj.url || null;
}
}
}

View File

@@ -16,7 +16,18 @@
*/
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { OAuthService, OAuthEvent, OAuthStorage, AUTH_CONFIG, TokenResponse, AuthConfig, OAuthLogger, OAuthErrorEvent, OAuthSuccessEvent, OAuthInfoEvent } from 'angular-oauth2-oidc';
import {
OAuthService,
OAuthEvent,
OAuthStorage,
AUTH_CONFIG,
TokenResponse,
AuthConfig,
OAuthLogger,
OAuthErrorEvent,
OAuthSuccessEvent,
OAuthInfoEvent
} from 'angular-oauth2-oidc';
import { of, Subject, timeout } from 'rxjs';
import { RedirectAuthService } from './redirect-auth.service';
import { AUTH_MODULE_CONFIG } from './auth-config';
@@ -43,18 +54,22 @@ describe('RedirectAuthService', () => {
retryLoginServiceSpy = jasmine.createSpyObj('RetryLoginService', ['tryToLoginTimes']);
timeSyncServiceSpy = jasmine.createSpyObj('TimeSyncService', ['checkTimeSync']);
oauthLoggerSpy = jasmine.createSpyObj('OAuthLogger', ['error', 'info', 'warn']);
oauthServiceSpy = jasmine.createSpyObj('OAuthService', [
'clearHashAfterLogin',
'configure',
'logOut',
'hasValidAccessToken',
'hasValidIdToken',
'setupAutomaticSilentRefresh',
'silentRefresh',
'refreshToken',
'getIdentityClaims',
'getAccessToken'
], { clockSkewInSec: 120, events: oauthEvents$, tokenValidationHandler: {} });
oauthServiceSpy = jasmine.createSpyObj(
'OAuthService',
[
'clearHashAfterLogin',
'configure',
'logOut',
'hasValidAccessToken',
'hasValidIdToken',
'setupAutomaticSilentRefresh',
'silentRefresh',
'refreshToken',
'getIdentityClaims',
'getAccessToken'
],
{ clockSkewInSec: 120, events: oauthEvents$, tokenValidationHandler: {} }
);
authConfigSpy = jasmine.createSpyObj('AuthConfig', ['sessionChecksEnabled']);
TestBed.configureTestingModule({
@@ -202,8 +217,14 @@ describe('RedirectAuthService', () => {
});
it('should logout user if token has expired due to local machine clock being out of sync', () => {
const mockTimeSync: TimeSync = { outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' };
const expectedError = new Error(`Token has expired due to local machine clock ${mockTimeSync.localDateTimeISO} being out of sync with server time ${mockTimeSync.serverDateTimeISO}`);
const mockTimeSync: TimeSync = {
outOfSync: true,
localDateTimeISO: '2024-10-10T22:00:18.621Z',
serverDateTimeISO: '2024-10-10T22:10:53.000Z'
};
const expectedError = new Error(
`Token has expired due to local machine clock ${mockTimeSync.localDateTimeISO} being out of sync with server time ${mockTimeSync.serverDateTimeISO}`
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(of(mockTimeSync));
@@ -375,7 +396,7 @@ describe('RedirectAuthService', () => {
expect(oauthServiceSpy.logOut).not.toHaveBeenCalled();
expect(oauthLoggerSpy.error).not.toHaveBeenCalled();
expect(await firstEventOccurPromise).toEqual(expectedFakeErrorEvent);;
expect(await firstEventOccurPromise).toEqual(expectedFakeErrorEvent);
try {
tick(1000);
@@ -387,7 +408,6 @@ describe('RedirectAuthService', () => {
}));
it('should logout user if the second time the refresh token failed', fakeAsync(async () => {
const expectedErrorCausedBySecondTokenRefreshError = new OAuthErrorEvent('token_refresh_error', { reason: 'second token refresh error' }, {});
oauthEvents$.next(new OAuthErrorEvent('token_refresh_error', { reason: 'error' }, {}));
@@ -398,8 +418,12 @@ describe('RedirectAuthService', () => {
}));
it('should logout user if token_refresh_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('token_refresh_error', { reason: 'error' }, {}));
@@ -408,8 +432,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if discovery_document_load_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('discovery_document_load_error', { reason: 'error' }, {}));
@@ -418,8 +446,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if code_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('code_error', { reason: 'error' }, {}));
@@ -428,8 +460,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if discovery_document_validation_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('discovery_document_validation_error', { reason: 'error' }, {}));
@@ -438,8 +474,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if jwks_load_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('jwks_load_error', { reason: 'error' }, {}));
@@ -448,8 +488,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if silent_refresh_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('silent_refresh_error', { reason: 'error' }, {}));
@@ -458,8 +502,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if user_profile_load_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('user_profile_load_error', { reason: 'error' }, {}));
@@ -468,8 +516,12 @@ describe('RedirectAuthService', () => {
});
it('should logout user if token_error is emitted because of clock out of sync', () => {
const expectedErrorMessage = new Error('OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z');
timeSyncServiceSpy.checkTimeSync.and.returnValue(of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync));
const expectedErrorMessage = new Error(
'OAuth error occurred due to local machine clock 2024-10-10T22:00:18.621Z being out of sync with server time 2024-10-10T22:10:53.000Z'
);
timeSyncServiceSpy.checkTimeSync.and.returnValue(
of({ outOfSync: true, localDateTimeISO: '2024-10-10T22:00:18.621Z', serverDateTimeISO: '2024-10-10T22:10:53.000Z' } as TimeSync)
);
oauthEvents$.next(new OAuthErrorEvent('token_error', { reason: 'error' }, {}));
@@ -479,11 +531,10 @@ describe('RedirectAuthService', () => {
it('should onLogout$ be emitted when logout event occur', () => {
let expectedLogoutIsEmitted = false;
service.onLogout$.subscribe(() => expectedLogoutIsEmitted = true);
service.onLogout$.subscribe(() => (expectedLogoutIsEmitted = true));
oauthEvents$.next(new OAuthInfoEvent('logout'));
expect(expectedLogoutIsEmitted).toBeTrue();
});
});

View File

@@ -18,9 +18,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
template: '<div data-automation-id="auth-confirmation"></div>',
changeDetection: ChangeDetectionStrategy.OnPush
template: '<div data-automation-id="auth-confirmation"></div>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AuthenticationConfirmationComponent {
constructor(){}
constructor() {}
}

View File

@@ -35,9 +35,7 @@ export class AuthenticationService implements AuthenticationServiceInterface, ee
constructor(private injector: Injector, private redirectAuthService: RedirectAuthService) {
this.redirectAuthService.onLogin.subscribe((value) => this.onLogin.next(value));
this.redirectAuthService.onTokenReceived.subscribe(
(value) => this.onTokenReceived.next(value)
);
this.redirectAuthService.onTokenReceived.subscribe((value) => this.onTokenReceived.next(value));
this.basicAlfrescoAuthService.onLogin.subscribe((value) => this.onLogin.next(value));

View File

@@ -32,11 +32,7 @@ import { OAuth2Service } from './oauth2.service';
@Injectable({ providedIn: 'root' })
export class IdentityGroupService implements IdentityGroupServiceInterface {
constructor(
private oAuth2Service: OAuth2Service,
private appConfigService: AppConfigService
) {}
constructor(private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {}
private get identityHost(): string {
return `${this.appConfigService.get('identityHost')}`;
@@ -125,18 +121,22 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
return this.getTotalGroupsCount().pipe(
switchMap((totalCount: IdentityGroupCountModel) =>
this.oAuth2Service.get<any[]>({ url, queryParams }).pipe(
map((response) => ({
entries: response,
pagination: {
skipCount: requestQuery.first,
maxItems: requestQuery.max,
count: totalCount.count,
hasMoreItems: false,
totalItems: totalCount.count
}
} as IdentityGroupQueryResponse))
))
this.oAuth2Service.get<any[]>({ url, queryParams }).pipe(
map(
(response) =>
({
entries: response,
pagination: {
skipCount: requestQuery.first,
maxItems: requestQuery.max,
count: totalCount.count,
hasMoreItems: false,
totalItems: totalCount.count
}
} as IdentityGroupQueryResponse)
)
)
)
);
}
@@ -222,20 +222,22 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
* @param roleNames Array of role names
* @returns True if the group has one or more of the roles, false otherwise
*/
checkGroupHasRole(groupId: string, roleNames: string[]): Observable<boolean> {
return this.getGroupRoles(groupId).pipe(map((groupRoles) => {
let hasRole = false;
if (groupRoles?.length > 0) {
roleNames.forEach((roleName: string) => {
const role = groupRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}));
checkGroupHasRole(groupId: string, roleNames: string[]): Observable<boolean> {
return this.getGroupRoles(groupId).pipe(
map((groupRoles) => {
let hasRole = false;
if (groupRoles?.length > 0) {
roleNames.forEach((roleName: string) => {
const role = groupRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
})
);
}
/**
@@ -246,11 +248,9 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
*/
getClientIdByApplicationName(applicationName: string): Observable<string> {
const url = `${this.identityHost}/clients`;
const queryParams = {clientId: applicationName};
const queryParams = { clientId: applicationName };
return this.oAuth2Service.get<any[]>({ url, queryParams }).pipe(
map((response) => response && response.length > 0 ? response[0].id : '')
);
return this.oAuth2Service.get<any[]>({ url, queryParams }).pipe(map((response) => (response && response.length > 0 ? response[0].id : '')));
}
/**
@@ -273,9 +273,7 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
* @returns True if the group has the client app, false otherwise
*/
checkGroupHasClientApp(groupId: string, clientId: string): Observable<boolean> {
return this.getClientRoles(groupId, clientId).pipe(
map((response) => response && response.length > 0)
);
return this.getClientRoles(groupId, clientId).pipe(map((response) => response && response.length > 0));
}
/**

View File

@@ -21,21 +21,31 @@ import { of, throwError } from 'rxjs';
import { IdentityRoleResponseModel, IdentityRoleService } from './identity-role.service';
export const mockIdentityRole1 = {
id: 'mock-id-1', name: 'Mock_Role_1', description: 'Mock desc1', clientRole: true, composite: false
id: 'mock-id-1',
name: 'Mock_Role_1',
description: 'Mock desc1',
clientRole: true,
composite: false
};
export const mockIdentityRole2 = {
id: 'mock-id-2', name: 'Mock_Role_2', description: 'Mock desc2', clientRole: false, composite: true
id: 'mock-id-2',
name: 'Mock_Role_2',
description: 'Mock desc2',
clientRole: false,
composite: true
};
export const mockIdentityRole3 = {
id: 'mock-id-3', name: 'Mock_Role_3', description: 'Mock desc3', clientRole: false, composite: false
id: 'mock-id-3',
name: 'Mock_Role_3',
description: 'Mock desc3',
clientRole: false,
composite: false
};
export const mockIdentityRoles = {
entries: [
mockIdentityRole1, mockIdentityRole2, mockIdentityRole3
],
entries: [mockIdentityRole1, mockIdentityRole2, mockIdentityRole3],
pagination: {
skipCount: 1,
maxItems: 5,
@@ -50,9 +60,7 @@ describe('IdentityRoleService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule
]
imports: [HttpClientModule]
});
service = TestBed.inject(IdentityRoleService);
});
@@ -75,31 +83,29 @@ describe('IdentityRoleService', () => {
statusText: 'Created'
});
spyOn(service, 'addRole').and.returnValue(of(response));
service.addRole(mockIdentityRole1).subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.status).toEqual(201);
expect(res.statusText).toEqual('Created');
done();
}
);
service.addRole(mockIdentityRole1).subscribe((res: any) => {
expect(res).toBeDefined();
expect(res.status).toEqual(201);
expect(res.statusText).toEqual('Created');
done();
});
});
it('Should not add role if error occurred', () => {
const errorResponse = new HttpErrorResponse({
error: 'test 404 error',
status: 404, statusText: 'Not Found'
status: 404,
statusText: 'Not Found'
});
spyOn(service, 'addRole').and.returnValue(throwError(errorResponse));
service.addRole(mockIdentityRole1)
.subscribe(
() => fail('expected an error'),
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('test 404 error');
}
);
service.addRole(mockIdentityRole1).subscribe(
() => fail('expected an error'),
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('test 404 error');
}
);
});
it('should be able to delete role', (done) => {
@@ -109,30 +115,28 @@ describe('IdentityRoleService', () => {
statusText: 'No Content'
});
spyOn(service, 'deleteRole').and.returnValue(of(response));
service.deleteRole(mockIdentityRole1).subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.status).toEqual(204);
expect(res.statusText).toEqual('No Content');
done();
}
);
service.deleteRole(mockIdentityRole1).subscribe((res: any) => {
expect(res).toBeDefined();
expect(res.status).toEqual(204);
expect(res.statusText).toEqual('No Content');
done();
});
});
it('Should not delete role if error occurred', () => {
const errorResponse = new HttpErrorResponse({
error: 'test 404 error',
status: 404, statusText: 'Not Found'
status: 404,
statusText: 'Not Found'
});
spyOn(service, 'deleteRole').and.returnValue(throwError(errorResponse));
service.deleteRole(mockIdentityRole1)
.subscribe(
() => fail('expected an error'),
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('test 404 error');
}
);
service.deleteRole(mockIdentityRole1).subscribe(
() => fail('expected an error'),
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('test 404 error');
}
);
});
});

View File

@@ -22,7 +22,13 @@ import { AppConfigService } from '../../app-config/app-config.service';
import { IdentityGroupModel } from '../models/identity-group.model';
import { IdentityRoleModel } from '../models/identity-role.model';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityJoinGroupRequestModel, IdentityUserServiceInterface, IdentityUserPasswordModel, IdentityUserQueryCloudRequestModel, IdentityUserQueryResponse } from '../interfaces/identity-user.service.interface';
import {
IdentityJoinGroupRequestModel,
IdentityUserServiceInterface,
IdentityUserPasswordModel,
IdentityUserQueryCloudRequestModel,
IdentityUserQueryResponse
} from '../interfaces/identity-user.service.interface';
import { JwtHelperService } from './jwt-helper.service';
import { OAuth2Service } from './oauth2.service';
@@ -30,11 +36,7 @@ import { OAuth2Service } from './oauth2.service';
providedIn: 'root'
})
export class IdentityUserService implements IdentityUserServiceInterface {
constructor(
private jwtHelperService: JwtHelperService,
private oAuth2Service: OAuth2Service,
private appConfigService: AppConfigService) { }
constructor(private jwtHelperService: JwtHelperService, private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {}
private get identityHost(): string {
return `${this.appConfigService.get('identityHost')}`;
@@ -86,7 +88,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
const url = this.buildUserUrl();
const queryParams = { username };
return this.oAuth2Service.get({url, queryParams });
return this.oAuth2Service.get({ url, queryParams });
}
/**
@@ -139,9 +141,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
* @returns True if the user has access, false otherwise
*/
checkUserHasClientApp(userId: string, clientId: string): Observable<boolean> {
return this.getClientRoles(userId, clientId).pipe(
map((clientRoles) => clientRoles.length > 0)
);
return this.getClientRoles(userId, clientId).pipe(map((clientRoles) => clientRoles.length > 0));
}
/**
@@ -181,11 +181,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
const url = `${this.identityHost}/clients`;
const queryParams = { clientId: applicationName };
return this.oAuth2Service
.get<any[]>({url, queryParams })
.pipe(
map((response) => response && response.length > 0 ? response[0].id : '')
);
return this.oAuth2Service.get<any[]>({ url, queryParams }).pipe(map((response) => (response && response.length > 0 ? response[0].id : '')));
}
/**
@@ -196,9 +192,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
* @returns True if the user has access, false otherwise
*/
checkUserHasApplicationAccess(userId: string, applicationName: string): Observable<boolean> {
return this.getClientIdByApplicationName(applicationName).pipe(
switchMap((clientId: string) => this.checkUserHasClientApp(userId, clientId))
);
return this.getClientIdByApplicationName(applicationName).pipe(switchMap((clientId: string) => this.checkUserHasClientApp(userId, clientId)));
}
/**
@@ -302,19 +296,21 @@ export class IdentityUserService implements IdentityUserServiceInterface {
* @returns True if the user has one of the roles, false otherwise
*/
checkUserHasRole(userId: string, roleNames: string[]): Observable<boolean> {
return this.getUserRoles(userId).pipe(map((userRoles: IdentityRoleModel[]) => {
let hasRole = false;
if (userRoles && userRoles.length > 0) {
roleNames.forEach((roleName: string) => {
const role = userRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}));
return this.getUserRoles(userId).pipe(
map((userRoles: IdentityRoleModel[]) => {
let hasRole = false;
if (userRoles && userRoles.length > 0) {
roleNames.forEach((roleName: string) => {
const role = userRoles.find(({ name }) => roleName === name);
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
})
);
}
/**
@@ -330,16 +326,19 @@ export class IdentityUserService implements IdentityUserServiceInterface {
return this.getTotalUsersCount().pipe(
switchMap((totalCount) =>
this.oAuth2Service.get<IdentityUserModel[]>({ url, queryParams }).pipe(
map((response) => ({
entries: response,
pagination: {
skipCount: requestQuery.first,
maxItems: requestQuery.max,
count: totalCount,
hasMoreItems: false,
totalItems: totalCount
}
} as IdentityUserQueryResponse))
map(
(response) =>
({
entries: response,
pagination: {
skipCount: requestQuery.first,
maxItems: requestQuery.max,
count: totalCount,
hasMoreItems: false,
totalItems: totalCount
}
} as IdentityUserQueryResponse)
)
)
)
);

View File

@@ -33,7 +33,7 @@ export class JwtHelperService {
static USER_PREFERRED_USERNAME = 'preferred_username';
static HXP_AUTHORIZATION = 'hxp_authorization';
private storageService: OAuthStorage = inject(OAuthStorage)
private storageService: OAuthStorage = inject(OAuthStorage);
/**
* Decodes a JSON web token into a JS object.

View File

@@ -36,17 +36,14 @@ export class OAuth2Service {
request<T>(opts: OAuth2RequestParams): Observable<T> {
const { httpMethod, url, bodyParam, queryParams } = opts;
return from(
this.adfHttpClient.request(
url,
{
httpMethod,
queryParams,
headerParams: {},
formParams: {},
bodyParam,
returnType: Object
}
)
this.adfHttpClient.request(url, {
httpMethod,
queryParams,
headerParams: {},
formParams: {},
bodyParam,
returnType: Object
})
);
}

View File

@@ -32,13 +32,9 @@ export interface TimeSync {
providedIn: 'root'
})
export class TimeSyncService {
private readonly _http: HttpClient;
constructor(
private _injector: Injector,
private _appConfigService: AppConfigService
) {
constructor(private _injector: Injector, private _appConfigService: AppConfigService) {
this._http = this._injector.get(HttpClient);
}
@@ -59,7 +55,7 @@ export class TimeSyncService {
serverTimeInMs = serverTimeResponse;
}
const adjustedServerTimeInMs = serverTimeInMs + (roundTripTimeInMs / 2);
const adjustedServerTimeInMs = serverTimeInMs + roundTripTimeInMs / 2;
const localCurrentTimeInMs = Date.now();
const timeOffsetInMs = Math.abs(localCurrentTimeInMs - adjustedServerTimeInMs);
const maxAllowedClockSkewInMs = maxAllowedClockSkewInSec * 1000;
@@ -71,7 +67,7 @@ export class TimeSyncService {
serverDateTimeISO: new Date(adjustedServerTimeInMs).toISOString()
};
}),
catchError(error => throwError(() => new Error(error)))
catchError((error) => throwError(() => new Error(error)))
);
}
@@ -82,9 +78,7 @@ export class TimeSyncService {
* @returns An Observable that emits a boolean indicating whether the local time is out of sync.
*/
isLocalTimeOutOfSync(maxAllowedClockSkewInSec: number): Observable<boolean> {
return this.checkTimeSync(maxAllowedClockSkewInSec).pipe(
map(sync => sync.outOfSync)
);
return this.checkTimeSync(maxAllowedClockSkewInSec).pipe(map((sync) => sync.outOfSync));
}
private getServerTime(): Observable<number> {

View File

@@ -27,9 +27,7 @@ export class UserAccessService {
private globalAccess: string[];
private applicationAccess: ApplicationAccessModel[];
constructor(private jwtHelperService: JwtHelperService,
private appConfigService: AppConfigService) {
}
constructor(private jwtHelperService: JwtHelperService, private appConfigService: AppConfigService) {}
fetchUserAccess() {
if (this.hasRolesInRealmAccess()) {
@@ -100,8 +98,10 @@ export class UserAccessService {
*/
hasApplicationAccess(appName: string, rolesToCheck: string[]): boolean {
if (rolesToCheck?.length > 0) {
const appAccess = this.hasRolesInRealmAccess() ? this.applicationAccess[appName] : this.applicationAccess.find((app: ApplicationAccessModel) => app.name === appName);
return appAccess ? appAccess.roles.some(appRole => rolesToCheck.includes(appRole)) : false;
const appAccess = this.hasRolesInRealmAccess()
? this.applicationAccess[appName]
: this.applicationAccess.find((app: ApplicationAccessModel) => app.name === appName);
return appAccess ? appAccess.roles.some((appRole) => rolesToCheck.includes(appRole)) : false;
}
return true;
}

View File

@@ -123,5 +123,5 @@
</ng-container>
<ng-template #buttonContent>
<ng-content></ng-content>
<ng-content />
</ng-template>

View File

@@ -23,7 +23,7 @@ import { DEFAULT_SEPARATOR } from '../card-view-textitem/card-view-textitem.comp
@Component({
selector: 'adf-card-view-item-dispatcher',
standalone: true,
template: '<ng-template #content></ng-template>'
template: '<ng-template #content />'
})
export class CardViewItemDispatcherComponent implements OnChanges {
@Input()

View File

@@ -71,11 +71,11 @@
border-bottom: 1px solid var(--adf-metadata-property-panel-border-color);
}
.adf-property-readonly-value {
color: var(--adf-metadata-property-panel-label-color);
.adf-property-readonly-value:disabled {
--mdc-filled-text-field-disabled-input-text-color: var(--adf-metadata-property-panel-title-color);
}
.adf-property-value-editable {
.adf-property-value-editable:not(:disabled) {
color: var(--adf-metadata-property-panel-title-color);
}

View File

@@ -27,7 +27,6 @@ export interface CardViewArrayItem {
}
export class CardViewArrayItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'array';
value: Observable<CardViewArrayItem[]>;
noOfItemsToDisplay: number;

View File

@@ -117,18 +117,20 @@ describe('CardViewBaseItemModel', () => {
const isValid = itemModel.isValid('test-against-this');
expect(isValid).toBe(false);
expect(itemModel.getValidationErrors('test-against-this')).toEqual([validator1, validator3 ]);
expect(itemModel.getValidationErrors('test-against-this')).toEqual([validator1, validator3]);
});
it('should validate field with special character and return false when there is REGEX constraint and requiresMatch is false', () => {
const constrainedProperties: CardViewItemProperties = {
...properties,
value: 'test.',
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: false }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: false }
}
]
};
const itemModel = new CarViewCustomItemModel(constrainedProperties);
@@ -138,11 +140,13 @@ describe('CardViewBaseItemModel', () => {
it('should validate field without special character and return true when there is REGEX constraint and requiresMatch is false', () => {
const constrainedProperties: CardViewItemProperties = {
...properties,
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: false }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: false }
}
]
};
const itemModel = new CarViewCustomItemModel(constrainedProperties);
@@ -152,11 +156,13 @@ describe('CardViewBaseItemModel', () => {
it('should validate field without special character and return false when there is REGEX constraint and requiresMatch is true', () => {
const constrainedProperties: CardViewItemProperties = {
...properties,
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: true }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: true }
}
]
};
const itemModel = new CarViewCustomItemModel(constrainedProperties);
@@ -167,11 +173,13 @@ describe('CardViewBaseItemModel', () => {
const constrainedProperties: CardViewItemProperties = {
...properties,
value: 'test.',
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: true }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '(.*[\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$)', requiresMatch: true }
}
]
};
const itemModel = new CarViewCustomItemModel(constrainedProperties);

View File

@@ -19,7 +19,6 @@ import { CardViewBoolItemModel } from './card-view-boolitem.model';
import { CardViewBoolItemProperties } from '../interfaces/card-view.interfaces';
describe('CardViewFloatItemModel', () => {
let properties: CardViewBoolItemProperties;
beforeEach(() => {

View File

@@ -19,7 +19,6 @@ import { CardViewFloatItemModel } from './card-view-floatitem.model';
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
describe('CardViewFloatItemModel', () => {
let properties: CardViewTextItemProperties;
beforeEach(() => {
@@ -43,7 +42,7 @@ describe('CardViewFloatItemModel', () => {
expect(itemModel.value).toBe(undefined);
});
it('isValid should return the validator\'s value', () => {
it("isValid should return the validator's value", () => {
const itemModel = new CardViewFloatItemModel(properties);
expect(itemModel.isValid(42)).toBe(true, 'For 42 it should be true');
@@ -63,11 +62,13 @@ describe('CardViewFloatItemModel', () => {
value: '42.42',
key: 'tribe',
dataType: 'd:float',
constraints: [{
id: 'constraint-id',
type: 'MINMAX',
parameters: { minValue: 10, maxValue: 40 }
}]
constraints: [
{
id: 'constraint-id',
type: 'MINMAX',
parameters: { minValue: 10, maxValue: 40 }
}
]
};
const itemModel = new CardViewFloatItemModel(constrainedProperties);

View File

@@ -19,7 +19,6 @@ import { CardViewIntItemModel } from './card-view-intitem.model';
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
describe('CardViewIntItemModel', () => {
let properties: CardViewTextItemProperties;
beforeEach(() => {
@@ -43,7 +42,7 @@ describe('CardViewIntItemModel', () => {
expect(itemModel.value).toBe(undefined);
});
it('isValid should return the validator\'s value', () => {
it("isValid should return the validator's value", () => {
const itemModel = new CardViewIntItemModel(properties);
expect(itemModel.isValid(42)).toBe(true, 'For 42 it should be true');
@@ -63,11 +62,13 @@ describe('CardViewIntItemModel', () => {
value: '20',
key: 'tribe',
dataType: 'd:float',
constraints: [{
id: 'constraint-id',
type: 'MINMAX',
parameters: { minValue: 10, maxValue: 15 }
}]
constraints: [
{
id: 'constraint-id',
type: 'MINMAX',
parameters: { minValue: 10, maxValue: 15 }
}
]
};
const itemModel = new CardViewIntItemModel(constrainedProperties);

View File

@@ -27,7 +27,6 @@ class TestPipe implements PipeTransform {
}
describe('CardViewTextItemModel', () => {
let properties: CardViewTextItemProperties;
beforeEach(() => {
@@ -39,7 +38,6 @@ describe('CardViewTextItemModel', () => {
});
describe('displayValue', () => {
it('should return the value if it is present', () => {
const itemModel = new CardViewTextItemModel(properties);
@@ -56,18 +54,14 @@ describe('CardViewTextItemModel', () => {
});
it('should apply a pipe on the value if it is present', () => {
properties.pipes = [
{ pipe: new TestPipe() }
];
properties.pipes = [{ pipe: new TestPipe() }];
const itemModel = new CardViewTextItemModel(properties);
expect(itemModel.displayValue).toBe('testpiped-Banuk');
});
it('should apply a pipe on the value with parameters if those are present', () => {
properties.pipes = [
{ pipe: new TestPipe(), params: ['withParams'] }
];
properties.pipes = [{ pipe: new TestPipe(), params: ['withParams'] }];
const itemModel = new CardViewTextItemModel(properties);
expect(itemModel.displayValue).toBe('testpiped-Banuk-withParams');
@@ -91,11 +85,13 @@ describe('CardViewTextItemModel', () => {
label: 'Tribe',
value: 'test',
key: 'tribe',
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '^(?=.*test).*', requiresMatch: true }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '^(?=.*test).*', requiresMatch: true }
}
]
};
const itemModel = new CardViewTextItemModel(constrainedProperties);
@@ -110,11 +106,13 @@ describe('CardViewTextItemModel', () => {
label: 'Tribe',
value: 'test',
key: 'tribe',
constraints: [{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '^(?=.*test).*', requiresMatch: false }
}]
constraints: [
{
id: 'constraint-id',
type: 'REGEX',
parameters: { expression: '^(?=.*test).*', requiresMatch: false }
}
]
};
const itemModel = new CardViewTextItemModel(constrainedProperties);

View File

@@ -20,9 +20,7 @@ import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
import { CardViewUpdateService, transformKeyToObject } from './card-view-update.service';
describe('CardViewUpdateService', () => {
describe('transformKeyToObject', () => {
it('should return the proper constructed value object for "dotless" keys', () => {
const valueObject = transformKeyToObject('property-key', 'property-value');
@@ -47,7 +45,6 @@ describe('CardViewUpdateService', () => {
});
describe('Service', () => {
let cardViewUpdateService: CardViewUpdateService;
const property: CardViewBaseItemModel = {
label: 'property-label',
@@ -63,23 +60,17 @@ describe('CardViewUpdateService', () => {
});
it('should send updated message with proper parameters', fakeAsync(() => {
cardViewUpdateService.itemUpdated$.subscribe(
( { target, changed } ) => {
expect(target).toBe(property);
expect(changed).toEqual({ 'property-key': 'changed-property-value' });
}
);
cardViewUpdateService.itemUpdated$.subscribe(({ target, changed }) => {
expect(target).toBe(property);
expect(changed).toEqual({ 'property-key': 'changed-property-value' });
});
cardViewUpdateService.update(property, 'changed-property-value');
}));
it('should send clicked message with proper parameters', fakeAsync(() => {
cardViewUpdateService.itemClicked$.subscribe(
( { target } ) => {
expect(target).toBe(property);
}
);
cardViewUpdateService.itemClicked$.subscribe(({ target }) => {
expect(target).toBe(property);
});
cardViewUpdateService.clicked(property);
}));
});

View File

@@ -18,7 +18,6 @@
import { CardViewItemValidator } from '../interfaces/card-view.interfaces';
export class CardViewItemFloatValidator implements CardViewItemValidator {
message = 'CORE.CARDVIEW.VALIDATORS.FLOAT_VALIDATION_ERROR';
isValid(value: any | any[]): boolean {

View File

@@ -18,7 +18,6 @@
import { CardViewItemValidator } from '../interfaces/card-view.interfaces';
export class CardViewItemIntValidator implements CardViewItemValidator {
message = 'CORE.CARDVIEW.VALIDATORS.INT_VALIDATION_ERROR';
isValid(value: any | any[]): boolean {
@@ -26,7 +25,7 @@ export class CardViewItemIntValidator implements CardViewItemValidator {
return value.every(this.isIntegerNumber);
}
return value === '' || !isNaN(value) && this.isIntegerNumber(value) && this.isNotOnlySpace(value);
return value === '' || (!isNaN(value) && this.isIntegerNumber(value) && this.isNotOnlySpace(value));
}
isIntegerNumber(value: any): boolean {

View File

@@ -27,11 +27,7 @@ export class CardViewItemPositiveIntValidator implements CardViewItemValidator {
const valueIsNotSet = value === '';
return valueIsNotSet ||
(
!isNaN(value) &&
this.isPositiveNumber(value)
);
return valueIsNotSet || (!isNaN(value) && this.isPositiveNumber(value));
}
private isPositiveNumber(value: any): boolean {

View File

@@ -20,7 +20,6 @@ import { Observable, of } from 'rxjs';
import { CommentsService } from '../interfaces/comments-service.interface';
export class CommentsServiceMock implements Partial<CommentsService> {
constructor() {}
get(_id: string): Observable<CommentModel[]> {
@@ -40,36 +39,38 @@ const commentUser = new User({
});
export const commentsResponseMock = {
getComments: () => of([
new CommentModel({
id: 1,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 3,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
})
]),
addComment: (message = 'test comment') => of(
new CommentModel({
id: 1,
message,
created: new Date(),
createdBy: commentUser,
isSelected: false
})
)
getComments: () =>
of([
new CommentModel({
id: 1,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 3,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
})
]),
addComment: (message = 'test comment') =>
of(
new CommentModel({
id: 1,
message,
created: new Date(),
createdBy: commentUser,
isSelected: false
})
)
};

View File

@@ -21,7 +21,6 @@ import { CommentsService } from '../interfaces/comments-service.interface';
import { testUser } from './comments.stories.mock';
export class CommentsServiceStoriesMock implements Partial<CommentsService> {
constructor() {}
get(_id: string): Observable<CommentModel[]> {
@@ -41,36 +40,38 @@ const commentUser = new User({
});
export const commentsResponseMock = {
getComments: () => of([
new CommentModel({
id: 1,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 3,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
})
]),
addComment: (message: string) => of(
new CommentModel({
id: 1,
message,
created: new Date(),
createdBy: testUser,
isSelected: false
})
)
getComments: () =>
of([
new CommentModel({
id: 1,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 3,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
})
]),
addComment: (message: string) =>
of(
new CommentModel({
id: 1,
message,
created: new Date(),
createdBy: testUser,
isSelected: false
})
)
};

View File

@@ -72,35 +72,34 @@ export const testUser: any = {
avatarId: '044'
};
export const commentsStoriesData: CommentModel[] = [
new CommentModel({
id: 1,
message: `I've done this task, what's next?`,
created: getDateXMinutesAgo(30),
createdBy: johnDoe,
isSelected: false
id: 1,
message: `I've done this task, what's next?`,
created: getDateXMinutesAgo(30),
createdBy: johnDoe,
isSelected: false
}),
new CommentModel({
id: 2,
message: `I've assigned you another one 🤠`,
created: getDateXMinutesAgo(15),
createdBy: janeEod,
isSelected: false
id: 2,
message: `I've assigned you another one 🤠`,
created: getDateXMinutesAgo(15),
createdBy: janeEod,
isSelected: false
}),
new CommentModel({
id: 3,
message: '+1',
created: getDateXMinutesAgo(12),
createdBy: robertSmith,
isSelected: false
id: 3,
message: '+1',
created: getDateXMinutesAgo(12),
createdBy: robertSmith,
isSelected: false
}),
new CommentModel({
id: 4,
message: 'Cheers',
created: new Date(),
createdBy: johnDoe,
isSelected: false
id: 4,
message: 'Cheers',
created: new Date(),
createdBy: johnDoe,
isSelected: false
})
];

View File

@@ -25,10 +25,10 @@ export class LogLevelsEnum extends Number {
}
export const logLevels: any[] = [
{level: LogLevelsEnum.TRACE, name: 'TRACE'},
{level: LogLevelsEnum.DEBUG, name: 'DEBUG'},
{level: LogLevelsEnum.INFO, name: 'INFO'},
{level: LogLevelsEnum.WARN, name: 'WARN'},
{level: LogLevelsEnum.ERROR, name: 'ERROR'},
{level: LogLevelsEnum.SILENT, name: 'SILENT'}
{ level: LogLevelsEnum.TRACE, name: 'TRACE' },
{ level: LogLevelsEnum.DEBUG, name: 'DEBUG' },
{ level: LogLevelsEnum.INFO, name: 'INFO' },
{ level: LogLevelsEnum.WARN, name: 'WARN' },
{ level: LogLevelsEnum.ERROR, name: 'ERROR' },
{ level: LogLevelsEnum.SILENT, name: 'SILENT' }
];

View File

@@ -21,7 +21,6 @@ import { Injectable } from '@angular/core';
providedIn: 'root'
})
export class CookieService {
cookieEnabled = false;
constructor() {
@@ -53,7 +52,7 @@ export class CookieService {
getItem(key: string): string | null {
const regexp = new RegExp('(?:' + key + '|;\\s*' + key + ')=(.*?)(?:;|$)', 'g');
const result = regexp.exec(document.cookie);
return (result === null) ? null : result[1];
return result === null ? null : result[1];
}
/**
@@ -65,9 +64,7 @@ export class CookieService {
* @param path "Pathname" to store the cookie
*/
setItem(key: string, data: string, expiration: Date | null = null, path: string | null = null): void {
document.cookie = `${key}=${data}` +
(expiration ? ';expires=' + expiration.toUTCString() : '') +
(path ? `;path=${path}` : ';path=/');
document.cookie = `${key}=${data}` + (expiration ? ';expires=' + expiration.toUTCString() : '') + (path ? `;path=${path}` : ';path=/');
}
/**
@@ -77,8 +74,7 @@ export class CookieService {
* @param path "Pathname" to store the cookie
*/
deleteCookie(key: string, path: string | null = null): void {
document.cookie = key + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' +
(path ? `;path=${path}` : ';path=/');
document.cookie = key + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' + (path ? `;path=${path}` : ';path=/');
}
/** Placeholder for testing purposes - do not use. */

View File

@@ -42,8 +42,7 @@ export class DownloadService {
}
if (blob) {
if (typeof window.navigator !== 'undefined' &&
window.navigator['msSaveOrOpenBlob']) {
if (window.navigator?.['msSaveOrOpenBlob']) {
window.navigator['msSaveOrOpenBlob'](blob, fileName);
} else {
const url = window.URL.createObjectURL(blob);

View File

@@ -17,10 +17,13 @@
import { Type } from '@angular/core';
const getType = (type: any): any => () => type;
const getType =
(type: any): any =>
() =>
type;
export interface DynamicComponentModel {
type: string;
type: string;
}
export type DynamicComponentResolveFunction = (model: DynamicComponentModel) => Type<any>;
@@ -31,7 +34,6 @@ export class DynamicComponentResolver {
}
export abstract class DynamicComponentMapper {
protected defaultValue: Type<any> = undefined;
protected types: { [key: string]: DynamicComponentResolveFunction } = {};

View File

@@ -26,7 +26,6 @@ export interface HighlightTransformResult {
providedIn: 'root'
})
export class HighlightTransformService {
/**
* Searches for `search` string(s) within `text` and highlights all occurrences.
*
@@ -42,7 +41,10 @@ export class HighlightTransformService {
if (search && text) {
// eslint-disable-next-line no-useless-escape
let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
pattern = pattern.split(' ').filter((t) => t.length > 0).join('|');
pattern = pattern
.split(' ')
.filter((t) => t.length > 0)
.join('|');
const regex = new RegExp(pattern, 'gi');
result = this.removeHtmlTags(text).replace(regex, (match) => {

View File

@@ -166,11 +166,9 @@ describe('LogService', () => {
it('message Observable', fakeAsync(() => {
appConfigService.config['logLevel'] = 'trace';
providesLogComponent.componentInstance.logService.onMessage.subscribe(
(message) => {
expect(message).toEqual({ text: 'Test message', type: 'LOG' });
}
);
providesLogComponent.componentInstance.logService.onMessage.subscribe((message) => {
expect(message).toEqual({ text: 'Test message', type: 'LOG' });
});
providesLogComponent.componentInstance.log();
}));

View File

@@ -24,14 +24,10 @@ import { TranslationService } from '../../translation/translation.service';
providedIn: 'root'
})
export class PageTitleService {
private originalTitle: string = '';
private translatedTitle: string = '';
constructor(
private titleService: Title,
private appConfig: AppConfigService,
private translationService: TranslationService) {
constructor(private titleService: Title, private appConfig: AppConfigService, private translationService: TranslationService) {
translationService.translate.onLangChange.subscribe(() => this.onLanguageChanged());
translationService.translate.onTranslationChange.subscribe(() => this.onLanguageChanged());
}

View File

@@ -178,7 +178,7 @@ export class ThumbnailService {
*/
public getMimeTypeIcon(mimeType: string): string {
const icon = this.mimeTypeIcons[mimeType];
return (icon || DEFAULT_ICON);
return icon || DEFAULT_ICON;
}
/**

View File

@@ -22,9 +22,7 @@ import { DomSanitizer } from '@angular/platform-browser';
providedIn: 'root'
})
export class UrlService {
constructor(private sanitizer: DomSanitizer) {
}
constructor(private sanitizer: DomSanitizer) {}
/**
* Creates a trusted object URL from the Blob.
@@ -37,6 +35,4 @@ export class UrlService {
const url = window.URL.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustUrl(url) as string;
}
}

View File

@@ -18,7 +18,6 @@
import { ObjectUtils } from './object-utils';
describe('ObjectUtils', () => {
it('should get top level property value', () => {
const obj = {
id: 1

View File

@@ -25,7 +25,6 @@ export class ObjectUtils {
* @returns object property value
*/
static getValue(target: any, key: string): any {
if (!target || !key) {
return undefined;
}
@@ -80,46 +79,38 @@ export class ObjectUtils {
}
static isBooleanObject(target: any): boolean {
return Object.values(target).every(value => typeof value === 'boolean');
return Object.values(target).every((value) => typeof value === 'boolean');
}
static booleanPrettify(target: any, enhancer?: (param: string) => string): string {
if (
!target ||
ObjectUtils.isEmpty(target) ||
!ObjectUtils.isBooleanObject(target)
) {
if (!target || ObjectUtils.isEmpty(target) || !ObjectUtils.isBooleanObject(target)) {
return '';
}
if (
!ObjectUtils.isObject(target) ||
!ObjectUtils.hasKeys(target)
) {
if (!ObjectUtils.isObject(target) || !ObjectUtils.hasKeys(target)) {
return target.toString();
}
const greenBorderWhiteCheckSymbol = '&#9989';
const redCrossSymbol = '&#10060';
target = Object.keys(target).map((key) => {
if (target[key]) {
if (enhancer) {
return `${greenBorderWhiteCheckSymbol} ${enhancer(key)}`;
} else {
return `${greenBorderWhiteCheckSymbol} ${key}`;
target = Object.keys(target)
.map((key) => {
if (target[key]) {
if (enhancer) {
return `${greenBorderWhiteCheckSymbol} ${enhancer(key)}`;
} else {
return `${greenBorderWhiteCheckSymbol} ${key}`;
}
}
}
if (enhancer) {
return `${redCrossSymbol} ${enhancer(key)}`;
} else {
return `${redCrossSymbol} ${key}`;
}
}).join('\n');
if (enhancer) {
return `${redCrossSymbol} ${enhancer(key)}`;
} else {
return `${redCrossSymbol} ${key}`;
}
})
.join('\n');
return target;
}

View File

@@ -18,7 +18,6 @@
import { ObjectUtils } from './object-utils';
export class StringUtils {
static capitalize(target: string): string {
return target.charAt(0).toUpperCase() + target.slice(1).toLowerCase();
}
@@ -37,7 +36,7 @@ export class StringUtils {
static removeAll(target: string, ...delimiters: string[]): string {
const delimiterObj = {};
delimiters.forEach(delimiter => {
delimiters.forEach((delimiter) => {
delimiterObj[delimiter] = '';
});
@@ -49,8 +48,6 @@ export class StringUtils {
const bagOfWords = redactedTarget.split(' ');
const capitalizedBagOfWords = bagOfWords.map((word) => StringUtils.capitalize(word));
return capitalizedBagOfWords.join(' ');
}
}

View File

@@ -15,32 +15,26 @@
* limitations under the License.
*/
import {
state,
style,
animate,
transition,
query,
group,
sequence,
AnimationStateMetadata,
AnimationTransitionMetadata
} from '@angular/animations';
import { state, style, animate, transition, query, group, sequence, AnimationStateMetadata, AnimationTransitionMetadata } from '@angular/animations';
export const contextMenuAnimation: ( AnimationStateMetadata | AnimationTransitionMetadata)[] = [
state('void', style({
opacity: 0,
transform: 'scale(0.01, 0.01)'
})),
transition('void => *', sequence([
query('.mat-mdc-menu-content', style({ opacity: 0 })),
animate('100ms linear', style({ opacity: 1, transform: 'scale(1, 0.5)' })),
group([
query('.mat-mdc-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)',
style({ opacity: 1 })
)),
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ transform: 'scale(1, 1)' }))
export const contextMenuAnimation: (AnimationStateMetadata | AnimationTransitionMetadata)[] = [
state(
'void',
style({
opacity: 0,
transform: 'scale(0.01, 0.01)'
})
),
transition(
'void => *',
sequence([
query('.mat-mdc-menu-content', style({ opacity: 0 })),
animate('100ms linear', style({ opacity: 1, transform: 'scale(1, 0.5)' })),
group([
query('.mat-mdc-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)', style({ opacity: 1 }))),
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ transform: 'scale(1, 1)' }))
])
])
])),
),
transition('* => void', animate('150ms 50ms linear', style({ opacity: 0 })))
];

View File

@@ -33,11 +33,7 @@ const DEFAULT_CONFIG: ContextMenuOverlayConfig = {
providedIn: 'root'
})
export class ContextMenuOverlayService {
constructor(
private injector: Injector,
private overlay: Overlay
) {}
constructor(private injector: Injector, private overlay: Overlay) {}
open(config: ContextMenuOverlayConfig): ContextMenuOverlayRef {
const overlayConfig = { ...DEFAULT_CONFIG, ...config };
@@ -53,12 +49,15 @@ export class ContextMenuOverlayService {
// prevent native contextmenu on overlay element if config.hasBackdrop is true
if (overlayConfig.hasBackdrop) {
// eslint-disable-next-line no-underscore-dangle
(overlay as any)._backdropElement
.addEventListener('contextmenu', (event) => {
(overlay as any)._backdropElement.addEventListener(
'contextmenu',
(event) => {
event.preventDefault();
// eslint-disable-next-line no-underscore-dangle
(overlay as any)._backdropClick.next(null);
}, true);
},
true
);
}
return overlayRef;
@@ -88,28 +87,31 @@ export class ContextMenuOverlayService {
}
private getOverlayConfig(config: ContextMenuOverlayConfig): OverlayConfig {
const { clientY, clientX } = config.source;
const { clientY, clientX } = config.source;
const fakeElement: any = {
getBoundingClientRect: (): ClientRect => ({
bottom: clientY,
height: 0,
left: clientX,
right: clientX,
top: clientY,
width: 0
} as any)
getBoundingClientRect: (): ClientRect =>
({
bottom: clientY,
height: 0,
left: clientX,
right: clientX,
top: clientY,
width: 0
} as any)
};
const positionStrategy = this.overlay
.position()
.flexibleConnectedTo(new ElementRef(fakeElement))
.withPositions([{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top'
}]);
.withPositions([
{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top'
}
]);
const overlayConfig = new OverlayConfig({
hasBackdrop: config.hasBackdrop,

View File

@@ -18,8 +18,7 @@
import { OverlayRef } from '@angular/cdk/overlay';
export class ContextMenuOverlayRef {
constructor(private overlayRef: OverlayRef) { }
constructor(private overlayRef: OverlayRef) {}
close(): void {
this.overlayRef.dispose();

View File

@@ -20,7 +20,6 @@ import { DataColumn } from '../data/data-column.model';
import { DataRow } from '../data/data-row.model';
export class DataCellEventModel {
readonly row: DataRow;
readonly col: DataColumn;
actions: any[];
@@ -30,14 +29,11 @@ export class DataCellEventModel {
this.col = col;
this.actions = actions || [];
}
}
export class DataCellEvent extends BaseEvent<DataCellEventModel> {
constructor(row: DataRow, col: DataColumn, actions: any[]) {
super();
this.value = new DataCellEventModel(row, col, actions);
}
}

View File

@@ -19,7 +19,6 @@ import { BaseEvent } from '../../events';
import { DataRow } from '../data/data-row.model';
export class DataRowActionModel {
row: DataRow;
action: any;
@@ -30,7 +29,6 @@ export class DataRowActionModel {
}
export class DataRowActionEvent extends BaseEvent<DataRowActionModel> {
// backwards compatibility with 1.2.0 and earlier
get args(): DataRowActionModel {
return this.value;
@@ -40,5 +38,4 @@ export class DataRowActionEvent extends BaseEvent<DataRowActionModel> {
super();
this.value = new DataRowActionModel(row, action);
}
}

View File

@@ -22,7 +22,7 @@ import { DataRow } from '../../data/data-row.model';
@Component({
selector: 'adf-datatable-row',
standalone: true,
template: `<ng-content></ng-content>`,
template: `<ng-content />`,
encapsulation: ViewEncapsulation.None,
host: {
class: 'adf-datatable-row',

View File

@@ -104,7 +104,7 @@
</ng-container>
<div *ngIf="col.header" class="adf-datatable-cell-value">
<ng-template [ngTemplateOutlet]="col.header" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
<ng-template [ngTemplateOutlet]="col.header" [ngTemplateOutletContext]="{$implicit: col}" />
</div>
<span
@@ -112,7 +112,7 @@
[class.adf-datatable__header--sorted-desc]="isColumnSorted(col, 'desc')">
</span>
<ng-template *ngIf="allowFiltering" [ngTemplateOutlet]="headerFilterTemplate" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
<ng-template *ngIf="allowFiltering" [ngTemplateOutlet]="headerFilterTemplate" [ngTemplateOutletContext]="{$implicit: col}" />
<span
*ngIf="col.draggable"
@@ -434,9 +434,8 @@
<div class="adf-no-content-container adf-datatable-cell" role="gridcell">
<ng-template *ngIf="noContentTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="noContentTemplate">
</ng-template>
<ng-content select="adf-empty-list"></ng-content>
[ngForTemplate]="noContentTemplate" />
<ng-content select="adf-empty-list" />
</div>
</div>
</ng-container>
@@ -448,8 +447,7 @@
<div class="adf-no-permission__cell adf-no-content-container adf-datatable-cell">
<ng-template *ngIf="noPermissionTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="noPermissionTemplate">
</ng-template>
[ngForTemplate]="noPermissionTemplate" />
</div>
</div>
</ng-template>
@@ -459,8 +457,7 @@
<div class="adf-no-content-container adf-datatable-cell">
<ng-template *ngIf="loadingTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="loadingTemplate">
</ng-template>
[ngForTemplate]="loadingTemplate" />
</div>
</div>
</ng-template>

View File

@@ -38,7 +38,7 @@ import { HarnessLoader } from '@angular/cdk/testing';
@Component({
selector: 'adf-custom-column-template-component',
standalone: true,
template: ` <ng-template #tmplRef></ng-template> `
template: ` <ng-template #tmplRef /> `
})
class CustomColumnTemplateComponent {
@ViewChild('tmplRef', { static: true }) templateRef: TemplateRef<any>;

View File

@@ -32,7 +32,6 @@ import { LocalizedDatePipe, TimeAgoPipe } from '../../../pipes';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DateCellComponent extends DataTableCellComponent implements OnInit {
@Input()
dateConfig: DateConfig;

View File

@@ -1,6 +1,6 @@
<div class="adf-empty-list_template">
<ng-content select="[adf-empty-list-header]"></ng-content>
<ng-content select="[adf-empty-list-body]"></ng-content>
<ng-content select="[adf-empty-list-footer]"></ng-content>
<ng-content></ng-content>
<ng-content select="[adf-empty-list-header]" />
<ng-content select="[adf-empty-list-body]" />
<ng-content select="[adf-empty-list-footer]" />
<ng-content />
</div>

View File

@@ -22,11 +22,12 @@ export const mockCarsData: any = [
{
car_id: 1,
car_name: 'Fiat 126p (Process)',
car_price: 599.00,
car_price: 599.0,
fuel_consumption: 5.25789,
is_available: 'false',
production_start: '1972-04-23',
description: 'The Fiat 126 (Type 126) is a four-passenger, rear-engine, city car manufactured and marketed by Fiat over a twenty-eight year production run from 1972 until 2000, over a single generation.',
description:
'The Fiat 126 (Type 126) is a four-passenger, rear-engine, city car manufactured and marketed by Fiat over a twenty-eight year production run from 1972 until 2000, over a single generation.',
icon: 'airport_shuttle',
wikipedia_link: 'https://en.wikipedia.org/wiki/Fiat_126'
},
@@ -48,7 +49,8 @@ export const mockCarsData: any = [
fuel_consumption: 6,
is_available: 'true',
production_start: '1998-06-25T12:25:20',
description: 'The Audi A3 is a subcompact executive/small family car (C-segment) manufactured and marketed by the German automaker Audi AG since September 1996, currently in its fourth generation.',
description:
'The Audi A3 is a subcompact executive/small family car (C-segment) manufactured and marketed by the German automaker Audi AG since September 1996, currently in its fourth generation.',
icon: 'directions_car',
wikipedia_link: 'https://en.wikipedia.org/wiki/Audi_A3'
}

View File

@@ -15,13 +15,7 @@
* limitations under the License.
*/
import {
ChangeDetectionStrategy,
Component,
ViewEncapsulation,
Input,
OnInit
} from '@angular/core';
import { ChangeDetectionStrategy, Component, ViewEncapsulation, Input, OnInit } from '@angular/core';
import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component';
import { DecimalConfig } from '../../data/data-column.model';
import { CommonModule } from '@angular/common';
@@ -36,7 +30,6 @@ import { CommonModule } from '@angular/common';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NumberCellComponent extends DataTableCellComponent implements OnInit {
@Input()
decimalConfig: DecimalConfig;

View File

@@ -19,7 +19,6 @@ import { BaseUIEvent } from '../../events';
import { DataRow } from './data-row.model';
export class DataRowEvent extends BaseUIEvent<DataRow> {
sender: any;
constructor(value: DataRow, domEvent: Event, sender?: any) {
@@ -28,5 +27,4 @@ export class DataRowEvent extends BaseUIEvent<DataRow> {
this.event = domEvent;
this.sender = sender;
}
}

View File

@@ -16,10 +16,5 @@
*/
export class DataSorting {
constructor(
public key?: string,
public direction?: string,
public options?: Intl.CollatorOptions
) {
}
constructor(public key?: string, public direction?: string, public options?: Intl.CollatorOptions) {}
}

View File

@@ -24,14 +24,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [EditJsonDialogComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
TranslateModule,
MatDialogModule,
MatButtonModule
],
imports: [CommonModule, FormsModule, ReactiveFormsModule, TranslateModule, MatDialogModule, MatButtonModule],
exports: [EditJsonDialogComponent]
})
export class EditJsonDialogModule {}

View File

@@ -17,10 +17,7 @@
import { Component, OnInit, OnChanges, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import {
EditJsonDialogComponent,
EditJsonDialogSettings
} from './edit-json.dialog';
import { EditJsonDialogComponent, EditJsonDialogSettings } from './edit-json.dialog';
@Component({
selector: 'adf-edit-json-dialog-storybook',
@@ -75,11 +72,7 @@ export class EditJsonDialogStorybookComponent implements OnInit, OnChanges {
.afterClosed()
.subscribe((value: string) => {
if (value) {
this._settings.value = JSON.stringify(
JSON.parse(value),
null,
' '
);
this._settings.value = JSON.stringify(JSON.parse(value), null, ' ');
}
});
}

View File

@@ -31,16 +31,13 @@ export interface EditJsonDialogSettings {
host: { class: 'adf-edit-json-dialog' }
})
export class EditJsonDialogComponent implements OnInit {
editable: boolean = false;
title: string = 'JSON';
@Input()
value: string = '';
constructor(
@Inject(MAT_DIALOG_DATA) private settings: EditJsonDialogSettings
) {}
constructor(@Inject(MAT_DIALOG_DATA) private settings: EditJsonDialogSettings) {}
ngOnInit() {
if (this.settings) {

View File

@@ -20,7 +20,6 @@ import { fakeAsync, tick } from '@angular/core/testing';
import { UploadDirective } from './upload.directive';
describe('UploadDirective', () => {
let directive: UploadDirective;
let nativeElement: any;
@@ -139,7 +138,7 @@ describe('UploadDirective', () => {
directive.enabled = true;
directive.mode = ['click'];
const files = [{}];
const event = {currentTarget: {files}, target: {value: '/testpath/document.pdf'}};
const event = { currentTarget: { files }, target: { value: '/testpath/document.pdf' } };
directive.onSelectFiles(event);
expect(event.target.value).toBe('');

View File

@@ -17,7 +17,6 @@
/** Base cancellable event implementation */
export class BaseEvent<T> {
private isDefaultPrevented: boolean = false;
value: T;
@@ -29,5 +28,4 @@ export class BaseEvent<T> {
preventDefault() {
this.isDefaultPrevented = true;
}
}

View File

@@ -4,14 +4,14 @@
<div *ngIf="hasTabs()" class="alfresco-tabs-widget">
<mat-tab-group>
<mat-tab *ngFor="let tab of visibleTabs()" [label]="tab.title | translate ">
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: tab.fields }"></ng-template>
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: tab.fields }" />
</mat-tab>
</mat-tab-group>
</div>
</div>
<div *ngIf="!formDefinition.hasTabs() && formDefinition.hasFields()">
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: formDefinition.fields }"></ng-template>
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: formDefinition.fields }" />
</div>
</div>

View File

@@ -7,11 +7,11 @@
data-automation-id="adf-inplace-input">
<mat-label data-automation-id="adf-inplace-input-label">
<ng-content select="[label]"></ng-content>
<ng-content select="[label]" />
</mat-label>
<mat-error data-automation-id="adf-inplace-input-error">
<ng-content select="[error]"></ng-content>
<ng-content select="[error]" />
</mat-error>
</mat-form-field>
</div>

View File

@@ -30,7 +30,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'text1',
@@ -54,7 +54,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'text2',
@@ -88,7 +88,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'multilinetext1',
@@ -109,7 +109,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'multilinetext2',
@@ -140,7 +140,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'number1',
@@ -160,7 +160,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'number2',
@@ -190,7 +190,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'checkbox1',
@@ -207,7 +207,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'checkbox2',
@@ -234,7 +234,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'dropdown1',
@@ -261,7 +261,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'dropdown2',
@@ -298,7 +298,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'amount1',
@@ -321,7 +321,7 @@ export const cloudFormMock = {
currency: '$'
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'amount2',
@@ -354,7 +354,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'date1',
@@ -375,7 +375,7 @@ export const cloudFormMock = {
dateDisplayFormat: 'D-M-YYYY'
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'date2',
@@ -406,7 +406,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'radiobuttons1',
@@ -442,7 +442,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'radiobuttons2',
@@ -488,7 +488,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'AttachFileFieldRepresentation',
id: 'attachfile1',
@@ -513,7 +513,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'AttachFileFieldRepresentation',
id: 'attachfile2',
@@ -548,7 +548,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'displayvalue1',
@@ -572,7 +572,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'displayvalue2',
@@ -606,7 +606,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'displaytext1',
@@ -624,7 +624,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'displaytext2',
@@ -691,7 +691,7 @@ export const fakeCloudForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
type: 'text',
id: 'firstName',
@@ -710,7 +710,7 @@ export const fakeCloudForm = {
regexPattern: null
}
],
2: [
'2': [
{
type: 'text',
id: 'lastName',
@@ -775,7 +775,7 @@ export const conditionalUploadWidgetsMock: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0xlk8n',
name: 'Text',
@@ -793,7 +793,7 @@ export const conditionalUploadWidgetsMock: any = {
}
}
],
2: [
'2': [
{
id: 'Attachfile0h9fr1',
name: 'Attach file',
@@ -863,7 +863,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'textField',
name: 'TEXT_FIELD.TITLE',
@@ -881,7 +881,7 @@ export const multilingualForm: any = {
}
}
],
2: []
'2': []
}
},
{
@@ -891,7 +891,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'fildUploadField',
name: 'FILE_UPLOAD_FIELD.TITLE',
@@ -909,7 +909,7 @@ export const multilingualForm: any = {
}
}
],
2: [
'2': [
{
id: 'amountField',
name: 'AMOUNT_FIELD.TITLE',
@@ -937,7 +937,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'dateField',
name: 'DATE_FIELD.TITLE',
@@ -955,7 +955,7 @@ export const multilingualForm: any = {
dateDisplayFormat: 'D-M-YYYY'
}
],
2: []
'2': []
}
}
],
@@ -980,7 +980,7 @@ export const fakeMetadataForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
type: 'text',
id: 'pfx_property_one',
@@ -999,7 +999,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
2: [
'2': [
{
type: 'boolean',
id: 'pfx_property_two',
@@ -1018,7 +1018,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
3: [
'3': [
{
id: 'content_form_nodes',
name: 'Nodes',
@@ -1046,7 +1046,7 @@ export const fakeMetadataForm = {
}
}
],
4: [
'4': [
{
id: 'pfx_property_three',
name: 'pfx_property_three',
@@ -1081,7 +1081,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
5: [
'5': [
{
id: 'pfx_property_four',
name: 'pfx_property_four',
@@ -1116,7 +1116,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
6: [
'6': [
{
id: 'pfx_property_five',
name: 'pfx_property_five',
@@ -1151,7 +1151,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
7: [
'7': [
{
id: 'cmfb85b2a7295ba41209750bca176ccaf9a',
name: 'File viewer',
@@ -1167,7 +1167,7 @@ export const fakeMetadataForm = {
}
}
],
8: [
'8': [
{
type: 'text',
id: 'pfx_property_six',
@@ -1186,7 +1186,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
9: [
'9': [
{
type: 'text',
id: 'pfx_property_seven',
@@ -1205,7 +1205,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
10: [
'10': [
{
type: 'text',
id: 'pfx_property_eight',
@@ -1248,7 +1248,7 @@ export const fakeViewerForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
id: 'content_form_nodes',
name: 'Nodes',
@@ -1276,7 +1276,7 @@ export const fakeViewerForm = {
}
}
],
2: [
'2': [
{
id: 'upload_widget',
name: 'Nodes',
@@ -1304,7 +1304,7 @@ export const fakeViewerForm = {
}
}
],
3: [
'3': [
{
id: 'cmfb85b2a7295ba41209750bca176ccaf9a',
name: 'File viewer',

View File

@@ -34,7 +34,7 @@ export const formDisplayValueVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0bq3ar',
name: 'Text',
@@ -49,7 +49,7 @@ export const formDisplayValueVisibility = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: [
'2': [
{
id: 'Displayvalue0g6092',
name: 'Display value',
@@ -103,7 +103,7 @@ export const formDisplayValueForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'DisplayValueColspan',
name: 'DisplayValueColspan',
@@ -240,7 +240,7 @@ export const formDisplayValueForm = {
}
}
],
2: [
'2': [
{
id: 'Text0howrc',
name: 'Text',
@@ -341,7 +341,7 @@ export const formDisplayValueCombinedVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0bq3ar',
name: 'Text',
@@ -356,7 +356,7 @@ export const formDisplayValueCombinedVisibility = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: [
'2': [
{
id: 'Displayvalue0g6092',
name: 'Display value',
@@ -401,7 +401,7 @@ export const formDisplayValueCombinedVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'TextTwo',
name: 'TextTwo',
@@ -416,7 +416,7 @@ export const formDisplayValueCombinedVisibility = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: []
'2': []
}
}
],
@@ -444,7 +444,7 @@ export const formNumberWidgetVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number1',
name: 'Number1',
@@ -461,7 +461,7 @@ export const formNumberWidgetVisibility = {
}
}
],
2: [
'2': [
{
id: 'Number2',
name: 'Number2',
@@ -519,7 +519,7 @@ export const formNumberTextJson = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'NumberColspan',
name: 'NumberColspan',
@@ -581,7 +581,7 @@ export const formNumberTextJson = {
}
}
],
2: [
'2': [
{
id: 'Text',
name: 'Text',
@@ -759,7 +759,7 @@ export const formRequiredNumberWidget = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number1',
name: 'Number1',
@@ -776,7 +776,7 @@ export const formRequiredNumberWidget = {
}
}
],
2: [
'2': [
{
id: 'Number2',
name: 'Number2',
@@ -835,7 +835,7 @@ export const colspanForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number0u0kiv',
name: 'NumberColspan',
@@ -862,7 +862,7 @@ export const colspanForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number00fuuk',
name: 'Number',
@@ -880,7 +880,7 @@ export const colspanForm = {
}
}
],
2: [
'2': [
{
id: 'Number03u9d4',
name: 'Number',
@@ -907,7 +907,7 @@ export const colspanForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text04sjhr',
name: 'TextColspan',
@@ -935,7 +935,7 @@ export const colspanForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text01dask',
name: 'Text',
@@ -953,7 +953,7 @@ export const colspanForm = {
}
}
],
2: [
'2': [
{
id: 'Text02ds21',
name: 'Text',
@@ -998,7 +998,7 @@ export const numberNotRequiredForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number0x8cbv',
name: 'Number',
@@ -1012,7 +1012,7 @@ export const numberNotRequiredForm = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: []
'2': []
}
}
],
@@ -1040,7 +1040,7 @@ export const numberMinMaxForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Number0him2z',
name: 'Number',
@@ -1057,7 +1057,7 @@ export const numberMinMaxForm = {
}
}
],
2: []
'2': []
}
}
],
@@ -1084,7 +1084,7 @@ export const textWidgetVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'textOne',
name: 'textOne',
@@ -1099,7 +1099,7 @@ export const textWidgetVisibility = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: [
'2': [
{
id: 'textTwo',
name: 'textTwo',
@@ -1132,7 +1132,7 @@ export const textWidgetVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'textThree',
name: 'textThree',
@@ -1166,7 +1166,7 @@ export const textWidgetVisibility = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: [
'2': [
{
id: 'textFour',
name: 'textFour',
@@ -1228,7 +1228,7 @@ export const numberWidgetVisibilityForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0hs0gt',
name: 'TextOne',
@@ -1277,7 +1277,7 @@ export const numberWidgetVisibilityForm = {
}
}
],
2: [
'2': [
{
id: 'Text0cuqet',
name: 'TextTwo',
@@ -1321,7 +1321,7 @@ export const radioWidgetVisibilityForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0cee7g',
name: 'Text',
@@ -1336,7 +1336,7 @@ export const radioWidgetVisibilityForm = {
params: { existingColspan: 1, maxColspan: 2 }
}
],
2: [
'2': [
{
id: 'Radiobuttons03rkbo',
name: 'Radio buttons',
@@ -1393,7 +1393,7 @@ export const customWidgetForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0vdi18',
name: 'herejustoshowstandardones',
@@ -1412,7 +1412,7 @@ export const customWidgetForm = {
}
}
],
2: [
'2': [
{
id: 'bananaforevah0k8gui',
name: 'bananaforevah',
@@ -1457,7 +1457,7 @@ export const customWidgetFormWithVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0vdi18',
name: 'herejustoshowstandardones',
@@ -1476,7 +1476,7 @@ export const customWidgetFormWithVisibility = {
}
}
],
2: [
'2': [
{
id: 'bananaforevah0k8gui',
name: 'bananaforevah',
@@ -1529,7 +1529,7 @@ export const formDateVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Date0hwq20',
name: 'Date',
@@ -1549,7 +1549,7 @@ export const formDateVisibility = {
dateDisplayFormat: 'YYYY-MM-DD'
}
],
2: [
'2': [
{
id: 'Text0pqd1u',
name: 'Text equal specific date',
@@ -1586,7 +1586,7 @@ export const formDateVisibility = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0uyqd3',
name: 'Text NOT equal specific date',
@@ -1614,7 +1614,7 @@ export const formDateVisibility = {
}
}
],
2: []
'2': []
}
}
],
@@ -1641,8 +1641,8 @@ export const amountWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [],
2: [
'1': [],
'2': [
{
id: 'Text0id3ic',
name: 'Text',
@@ -1669,7 +1669,7 @@ export const amountWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Amount0kceqc',
name: 'Amount',
@@ -1707,7 +1707,7 @@ export const amountWidgetFormVisibilityMock = {
currency: '$'
}
],
2: [
'2': [
{
id: 'Number0yggl7',
name: 'Number',
@@ -1751,7 +1751,7 @@ export const checkboxWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Checkbox0pr51m',
name: 'Checkbox1',
@@ -1766,7 +1766,7 @@ export const checkboxWidgetFormVisibilityMock = {
}
}
],
2: [
'2': [
{
id: 'Checkbox0fp0zf',
name: 'Checkbox2',
@@ -1790,7 +1790,7 @@ export const checkboxWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Checkbox0lb7ze',
name: 'Checkbox',
@@ -1813,7 +1813,7 @@ export const checkboxWidgetFormVisibilityMock = {
}
}
],
2: []
'2': []
}
}
],
@@ -1840,7 +1840,7 @@ export const dateWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text5asd0a',
name: 'Text',
@@ -1858,7 +1858,7 @@ export const dateWidgetFormVisibilityMock = {
}
}
],
2: [
'2': [
{
id: 'Date8wbe3d',
name: 'Date',
@@ -1912,7 +1912,7 @@ export const multilineWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'MultilineTextId',
name: 'Multi Line Label',
@@ -1976,7 +1976,7 @@ export const multilineWidgetFormVisibilityMock = {
}
}
],
2: []
'2': []
}
}
],
@@ -2011,7 +2011,7 @@ export const displayTextWidgetFormVisibilityMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0tzu53',
name: 'Text',
@@ -2029,7 +2029,7 @@ export const displayTextWidgetFormVisibilityMock = {
}
}
],
2: [
'2': [
{
id: 'Displaytext0q4w02',
name: 'Display text',
@@ -2051,7 +2051,7 @@ export const displayTextWidgetFormVisibilityMock = {
}
}
],
3: [
'3': [
{
id: 'Displaytext8bac2e',
name: 'Display text',
@@ -2106,7 +2106,7 @@ export const displayBigDecimalWidgetMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Decimal0tzu53',
name: 'Bigdecimal',

View File

@@ -30,7 +30,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'text1',
@@ -54,7 +54,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'text2',
@@ -88,7 +88,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'multilinetext1',
@@ -109,7 +109,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'multilinetext2',
@@ -140,7 +140,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'number1',
@@ -160,7 +160,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'number2',
@@ -190,7 +190,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'checkbox1',
@@ -207,7 +207,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'checkbox2',
@@ -234,7 +234,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'dropdown1',
@@ -261,7 +261,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'dropdown2',
@@ -298,7 +298,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'amount1',
@@ -321,7 +321,7 @@ export const cloudFormMock = {
currency: '$'
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'amount2',
@@ -354,7 +354,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'date1',
@@ -375,7 +375,7 @@ export const cloudFormMock = {
dateDisplayFormat: 'D-M-YYYY'
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'date2',
@@ -406,7 +406,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'radiobuttons1',
@@ -442,7 +442,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'radiobuttons2',
@@ -488,7 +488,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'AttachFileFieldRepresentation',
id: 'attachfile1',
@@ -513,7 +513,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'AttachFileFieldRepresentation',
id: 'attachfile2',
@@ -548,7 +548,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'displayvalue1',
@@ -572,7 +572,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'displayvalue2',
@@ -606,7 +606,7 @@ export const cloudFormMock = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'displaytext1',
@@ -624,7 +624,7 @@ export const cloudFormMock = {
}
}
],
2: [
'2': [
{
fieldType: 'FormFieldRepresentation',
id: 'displaytext2',
@@ -691,7 +691,7 @@ export const fakeCloudForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
type: 'text',
id: 'firstName',
@@ -710,7 +710,7 @@ export const fakeCloudForm = {
regexPattern: null
}
],
2: [
'2': [
{
type: 'text',
id: 'lastName',
@@ -775,7 +775,7 @@ export const conditionalUploadWidgetsMock: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'Text0xlk8n',
name: 'Text',
@@ -793,7 +793,7 @@ export const conditionalUploadWidgetsMock: any = {
}
}
],
2: [
'2': [
{
id: 'Attachfile0h9fr1',
name: 'Attach file',
@@ -863,7 +863,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'textField',
name: 'TEXT_FIELD.TITLE',
@@ -881,7 +881,7 @@ export const multilingualForm: any = {
}
}
],
2: []
'2': []
}
},
{
@@ -891,7 +891,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'fildUploadField',
name: 'FILE_UPLOAD_FIELD.TITLE',
@@ -909,7 +909,7 @@ export const multilingualForm: any = {
}
}
],
2: [
'2': [
{
id: 'amountField',
name: 'AMOUNT_FIELD.TITLE',
@@ -937,7 +937,7 @@ export const multilingualForm: any = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'dateField',
name: 'DATE_FIELD.TITLE',
@@ -955,7 +955,7 @@ export const multilingualForm: any = {
dateDisplayFormat: 'D-M-YYYY'
}
],
2: []
'2': []
}
}
],
@@ -980,7 +980,7 @@ export const fakeMetadataForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
type: 'text',
id: 'pfx_property_one',
@@ -999,7 +999,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
2: [
'2': [
{
type: 'boolean',
id: 'pfx_property_two',
@@ -1018,7 +1018,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
3: [
'3': [
{
id: 'content_form_nodes',
name: 'Nodes',
@@ -1046,7 +1046,7 @@ export const fakeMetadataForm = {
}
}
],
4: [
'4': [
{
id: 'pfx_property_three',
name: 'pfx_property_three',
@@ -1081,7 +1081,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
5: [
'5': [
{
id: 'pfx_property_four',
name: 'pfx_property_four',
@@ -1116,7 +1116,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
6: [
'6': [
{
id: 'pfx_property_five',
name: 'pfx_property_five',
@@ -1151,7 +1151,7 @@ export const fakeMetadataForm = {
restLabelProperty: null
}
],
7: [
'7': [
{
id: 'cmfb85b2a7295ba41209750bca176ccaf9a',
name: 'File viewer',
@@ -1167,7 +1167,7 @@ export const fakeMetadataForm = {
}
}
],
8: [
'8': [
{
type: 'text',
id: 'pfx_property_six',
@@ -1186,7 +1186,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
9: [
'9': [
{
type: 'text',
id: 'pfx_property_seven',
@@ -1205,7 +1205,7 @@ export const fakeMetadataForm = {
regexPattern: null
}
],
10: [
'10': [
{
type: 'text',
id: 'pfx_property_eight',
@@ -1249,7 +1249,7 @@ export const mockDisplayExternalPropertyForm = {
tab: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'DisplayExternalProperty02kj65',
name: 'Display External Property',
@@ -1267,7 +1267,7 @@ export const mockDisplayExternalPropertyForm = {
value: 'hr'
}
],
2: [
'2': [
{
id: 'DisplayExternalProperty0ei65x',
name: 'Display External Property',
@@ -1325,7 +1325,7 @@ export const fakeViewerForm = {
name: 'Label',
tab: null,
fields: {
1: [
'1': [
{
id: 'content_form_nodes',
name: 'Nodes',
@@ -1353,7 +1353,7 @@ export const fakeViewerForm = {
}
}
],
2: [
'2': [
{
id: 'upload_widget',
name: 'Nodes',
@@ -1381,7 +1381,7 @@ export const fakeViewerForm = {
}
}
],
3: [
'3': [
{
id: 'cmfb85b2a7295ba41209750bca176ccaf9a',
name: 'File viewer',

View File

@@ -259,7 +259,7 @@ export class FormFieldModel extends FormWidgetModel {
private getDefaultDateFormat(jsonField: any): string {
let originalType = jsonField.type;
if (FormFieldTypes.isReadOnlyType(jsonField.type) && jsonField.params && jsonField.params.field) {
if (FormFieldTypes.isReadOnlyType(jsonField.type) && jsonField.params?.field) {
originalType = jsonField.params.field.type;
}
return originalType === FormFieldTypes.DATETIME ? this.defaultDateTimeFormat : this.defaultDateFormat;

View File

@@ -15,12 +15,11 @@
* limitations under the License.
*/
/* eslint-disable @angular-eslint/component-selector */
/* eslint-disable @angular-eslint/component-selector */
import { FormOutcomeModel } from './form-outcome.model';
export class FormOutcomeEvent {
private readonly _outcome: FormOutcomeModel;
private _defaultPrevented: boolean = false;
@@ -39,5 +38,4 @@ export class FormOutcomeEvent {
preventDefault() {
this._defaultPrevented = true;
}
}

View File

@@ -19,7 +19,6 @@ import { FormOutcomeModel } from './form-outcome.model';
import { FormModel } from './form.model';
describe('FormOutcomeModel', () => {
it('should setup with json config', () => {
const json = {
id: '<id>',

View File

@@ -19,7 +19,6 @@ import { FormWidgetModel } from './form-widget.model';
import { FormModel } from './form.model';
describe('FormWidgetModel', () => {
class FormWidgetModelMock extends FormWidgetModel {
constructor(form: FormModel, json: any) {
super(form, json);

View File

@@ -21,7 +21,6 @@ import { FormFieldModel } from './form-field.model';
import { TabModel } from './tab.model';
describe('TabModel', () => {
it('should setup with json config', () => {
const json = {
id: '<id>',

View File

@@ -15,13 +15,12 @@
* limitations under the License.
*/
/* eslint-disable @angular-eslint/component-selector */
/* eslint-disable @angular-eslint/component-selector */
import { WidgetVisibilityModel } from '../../../models/widget-visibility.model';
import { FormWidgetModel } from './form-widget.model';
export class TabModel extends FormWidgetModel {
title: string;
isVisible: boolean = true;
visibilityCondition: WidgetVisibilityModel;

View File

@@ -42,8 +42,8 @@ export class InputMaskDirective implements OnChanges, ControlValueAccessor {
};
private translationMask = {
0: { pattern: /\d/ },
9: { pattern: /\d/, optional: true },
'0': { pattern: /\d/ },
'9': { pattern: /\d/, optional: true },
'#': { pattern: /\d/, recursive: true },
A: { pattern: /[a-zA-Z0-9]/ },
S: { pattern: /[a-zA-Z]/ }

View File

@@ -19,12 +19,10 @@ import { FormModel } from '../components/widgets/core/form.model';
import { FormEvent } from './form.event';
export class FormErrorEvent extends FormEvent {
readonly error: any;
constructor(form: FormModel, error: any) {
super(form);
this.error = error;
}
}

View File

@@ -19,12 +19,10 @@ import { FormFieldModel } from '../components/widgets/core/form-field.model';
import { FormEvent } from './form.event';
export class FormFieldEvent extends FormEvent {
readonly field: FormFieldModel;
constructor(form: any, field: FormFieldModel) {
super(form);
this.field = field;
}
}

View File

@@ -19,7 +19,6 @@ import { FormFieldEvent } from './form-field.event';
import { FormEvent } from './form.event';
export class FormRulesEvent extends FormFieldEvent {
readonly type: string;
readonly event: Event;
@@ -28,5 +27,4 @@ export class FormRulesEvent extends FormFieldEvent {
this.type = type;
this.event = event;
}
}

View File

@@ -16,7 +16,6 @@
*/
export class FormEvent {
private isDefaultPrevented: boolean = false;
readonly form: any;

View File

@@ -19,11 +19,9 @@ import { FormFieldModel } from '../components/widgets/core/form-field.model';
import { FormFieldEvent } from './form-field.event';
export class ValidateFormFieldEvent extends FormFieldEvent {
isValid = true;
constructor(form: any, field: FormFieldModel) {
super(form, field);
}
}

View File

@@ -19,7 +19,6 @@ import { FormEvent } from './form.event';
import { FormFieldModel } from '../components/widgets/core/form-field.model';
export class ValidateFormEvent extends FormEvent {
isValid = true;
errorsField: FormFieldModel[] = [];

View File

@@ -58,7 +58,7 @@ export class WidgetVisibilityModel {
return this.json.leftValue;
} else if (this.leftFormFieldId) {
return this.leftFormFieldId;
} else if(this.leftRestResponseId){
} else if (this.leftRestResponseId) {
return this.leftRestResponseId;
}
return null;
@@ -91,7 +91,7 @@ export class WidgetVisibilityModel {
return this.json.rightValue;
} else if (this.rightFormFieldId) {
return this.rightFormFieldId;
} else if(this.rightRestResponseId){
} else if (this.rightRestResponseId) {
return this.rightRestResponseId;
}
return null;

View File

@@ -29,7 +29,6 @@ import { FormFieldTypes } from '../components/widgets';
providedIn: 'root'
})
export class FormRenderingService extends DynamicComponentMapper {
protected defaultValue: Type<any> = widgets.UnknownWidgetComponent;
protected types: { [key: string]: DynamicComponentResolveFunction } = {
[FormFieldTypes.TEXT]: DynamicComponentResolver.fromType(widgets.TextWidgetComponent),

View File

@@ -499,7 +499,7 @@ describe('WidgetVisibilityCloudService', () => {
value: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
fieldType: 'FormFieldRepresentation',
id: 'FIELD_FORM_EMPTY',
@@ -613,7 +613,7 @@ describe('WidgetVisibilityCloudService', () => {
value: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'radioboxField',
name: 'radioboxField test',
@@ -777,7 +777,7 @@ describe('WidgetVisibilityCloudService', () => {
value: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'CheckboxOne',
name: 'CheckboxOne',
@@ -797,7 +797,7 @@ describe('WidgetVisibilityCloudService', () => {
visibilityCondition: null
}
],
2: [
'2': [
{
id: 'CheckboxThree',
name: 'CheckboxThree',

View File

@@ -757,7 +757,7 @@ describe('WidgetVisibilityService', () => {
value: null,
numberOfColumns: 2,
fields: {
1: [
'1': [
{
id: 'radioboxField',
name: 'radioboxField test',

View File

@@ -30,13 +30,13 @@
<ng-template #toolbarActions>
<div class="adf-toolbar-actions">
<ng-content select="[adf-toolbar-actions]"></ng-content>
<ng-content select="[adf-toolbar-actions]" />
</div>
</ng-template>
<ng-template #navbarTemplate>
<adf-navbar [items]="navbarItems">
<ng-content select="adf-navbar-item"></ng-content>
<ng-content select="adf-navbar-item" />
</adf-navbar>
</ng-template>

View File

@@ -1,4 +1,4 @@
<adf-navbar-item *ngFor="let item of items"
[routerLink]="item.routerLink"
[label]="item.label" />
<ng-content></ng-content>
<ng-content />

View File

@@ -1,12 +1,12 @@
<div *ngIf="showHeader" class="adf-info-drawer-layout-header">
<div class="adf-info-drawer-layout-header-title">
<ng-content select="[info-drawer-node-icon]"></ng-content>
<ng-content select="[info-drawer-title]"></ng-content>
<ng-content select="[info-drawer-node-icon]" />
<ng-content select="[info-drawer-title]" />
</div>
<div class="adf-info-drawer-layout-header-buttons">
<ng-content select="[info-drawer-buttons]"></ng-content>
<ng-content select="[info-drawer-buttons]" />
</div>
</div>
<div class="adf-info-drawer-layout-content">
<ng-content select="[info-drawer-content]"></ng-content>
<ng-content select="[info-drawer-content]" />
</div>

View File

@@ -2,8 +2,8 @@
<img *ngIf="icon" class="adf-info-drawer-icon" alt="{{ 'INFO_DRAWER.ICON' | translate }}" src="{{ icon }}" info-drawer-node-icon>
<div *ngIf="title" role="heading" aria-level="1" title="{{ title | translate }}" info-drawer-title>{{ title | translate }}</div>
<ng-content *ngIf="!title" info-drawer-title select="[info-drawer-title]"></ng-content>
<ng-content info-drawer-buttons select="[info-drawer-buttons]"></ng-content>
<ng-content *ngIf="!title" info-drawer-title select="[info-drawer-title]" />
<ng-content info-drawer-buttons select="[info-drawer-buttons]" />
<ng-container info-drawer-content *ngIf="showTabLayout(); then tabLayout else singleLayout" />
<ng-template #tabLayout>
@@ -28,6 +28,6 @@
</ng-template>
<ng-template #singleLayout>
<ng-content select="[info-drawer-content]"></ng-content>
<ng-content select="[info-drawer-content]" />
</ng-template>
</adf-info-drawer-layout>

View File

@@ -40,7 +40,7 @@ import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'adf-info-drawer-tab',
standalone: true,
template: '<ng-template><ng-content></ng-content></ng-template>',
template: '<ng-template><ng-content /></ng-template>',
encapsulation: ViewEncapsulation.None
})
export class InfoDrawerTabComponent {

View File

@@ -36,7 +36,7 @@
{{ title }}
</h1>
<ng-content></ng-content>
<ng-content />
<button
*ngIf="showSidenavToggle && position === 'end'"

View File

@@ -6,12 +6,12 @@
[@sidenavAnimation]="sidenavAnimationState"
[opened]="!isMobileScreenSize || !hideSidenav"
[mode]="isMobileScreenSize ? 'over' : 'side'">
<ng-content sidenav select="[app-layout-navigation]"></ng-content>
<ng-content sidenav select="[app-layout-navigation]" />
</mat-sidenav>
<div>
<div class="adf-container-full-width" [@contentAnimationLeft]="getContentAnimationState()">
<ng-content select="[app-layout-content]"></ng-content>
<ng-content select="[app-layout-content]" />
</div>
</div>
</mat-sidenav-container>

Some files were not shown because too many files have changed in this diff Show More