[ACS-3339] Manage Rules screen / listing - Manage rules screen template & minimal listing (#2596)

* First commit: rules listing

* [ACS-3339] Manage Rules screen / listing - Manage rules screen template & minimal listing

* some changes for rule model

* some changes requested for PR

* a couple more fixes

* removed initial value from aca-rule-details

* spelling error fixed
This commit is contained in:
Nikita Maliarchuk
2022-08-19 18:27:21 +02:00
committed by GitHub
parent 9a650f5265
commit 5f009e89fa
19 changed files with 683 additions and 34 deletions

View File

@@ -62,6 +62,17 @@
},
"NO_CONDITIONS": "No conditions",
"NO_CONDITIONS_IN_GROUP": "No conditions in the group"
},
"MANAGE_RULES": {
"TOOLBAR": {
"BREADCRUMB": {
"RULES": "rules"
}
},
"EMPTY_RULES_LIST": {
"TITLE": "The list is empty",
"SUBTITLE": "There are no rules defined for this folder yet."
}
}
}
}

View File

@@ -24,17 +24,20 @@
*/
import { CoreModule, TranslationService } from '@alfresco/adf-core';
import { ExtensionService, provideExtensionConfig } from '@alfresco/adf-extensions';
import { ExtensionService, ExtensionsModule, provideExtensionConfig } from '@alfresco/adf-extensions';
import { NgModule } from '@angular/core';
import * as rules from './folder-rules.rules';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { EditRuleDialogSmartComponent } from './rule-details/edit-rule-dialog.smart-component';
import { ManageRulesSmartComponent } from './manage-rules/manage-rules.smart-component';
import { RuleCompositeConditionUiComponent } from './rule-details/conditions/rule-composite-condition.ui-component';
import { RuleDetailsUiComponent } from './rule-details/rule-details.ui-component';
import { RuleSimpleConditionUiComponent } from './rule-details/conditions/rule-simple-condition.ui-component';
import { GenericErrorModule, PageLayoutModule } from '@alfresco/aca-shared';
import { BreadcrumbModule, DocumentListModule } from '@alfresco/adf-content-services';
import { RuleListItemUiComponent } from './rules-list/rule/rule-list-item.ui-component';
import { RulesListUiComponent } from './rules-list/rules-list.ui-component';
const routes: Routes = [
{
@@ -45,13 +48,24 @@ const routes: Routes = [
@NgModule({
providers: [provideExtensionConfig(['folder-rules.plugin.json'])],
imports: [CommonModule, RouterModule.forChild(routes), CoreModule.forChild()],
imports: [
CommonModule,
RouterModule.forChild(routes),
CoreModule.forChild(),
PageLayoutModule,
BreadcrumbModule,
DocumentListModule,
ExtensionsModule,
GenericErrorModule
],
declarations: [
EditRuleDialogSmartComponent,
ManageRulesSmartComponent,
RuleCompositeConditionUiComponent,
RuleDetailsUiComponent,
RuleSimpleConditionUiComponent
RuleSimpleConditionUiComponent,
RulesListUiComponent,
RuleListItemUiComponent
]
})
export class AcaFolderRulesModule {

View File

@@ -0,0 +1,56 @@
<aca-page-layout>
<aca-page-layout-header>
<adf-toolbar class="adf-toolbar--inline">
<button mat-icon-button (click)="goBack()">
<mat-icon>arrow_back</mat-icon>
</button>
</adf-toolbar>
<adf-breadcrumb root="{{'ACA_FOLDER_RULES.ACTIONS.MANAGE_RULES' | translate}}"></adf-breadcrumb>
</aca-page-layout-header>
<aca-page-layout-content>
<div class="main-content">
<ng-container *ngIf="isLoading$ | async; else onLoaded">
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
</ng-container>
<ng-template #onLoaded>
<ng-container *ngIf="folderInfo$ | async; else genericError">
<adf-toolbar class="adf-toolbar--inline aca-rules-actions-bar">
<adf-toolbar-title>
<mat-icon class="icon-aligner">folder</mat-icon>
<adf-breadcrumb root="{{ (folderInfo$ | async).name }}:{{'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.BREADCRUMB.RULES' | translate}}"
class="aca-rules-breadcrumb"></adf-breadcrumb>
</adf-toolbar-title>
</adf-toolbar>
<mat-divider></mat-divider>
<div class="aca-manage-rules-container" *ngIf="(rules$ | async).length > 0 ; else emptyContent">
<aca-rules-list [rules]="rules$ | async" (ruleSelected)="onRuleSelected($event)" [selectedRule]="selectedRule"></aca-rules-list>
<aca-rule-details [readOnly]="true" ></aca-rule-details>
</div>
<ng-template #emptyContent>
<adf-empty-content
icon="library_books"
[title]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.TITLE' | translate"
[subtitle]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.SUBTITLE' | translate"
>
</adf-empty-content>
</ng-template>
</ng-container>
<ng-template #genericError>
<aca-page-layout-error>
<aca-generic-error></aca-generic-error>
</aca-page-layout-error>
</ng-template>
</ng-template>
</div>
</aca-page-layout-content>
</aca-page-layout>

View File

@@ -0,0 +1,19 @@
.aca-rules-actions-bar {
padding: 0 30px;
.aca-rules-breadcrumb {
margin-left: 18px;
}
.icon-aligner{
margin-top: 4px;
}
}
.aca-manage-rules-container {
display: grid;
grid-template-columns: 1fr 2fr;
padding: 32px;
overflow: scroll;
}

View File

@@ -0,0 +1,134 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { AcaFolderRulesModule, ManageRulesSmartComponent } from '@alfresco/aca-folder-rules';
import { DebugElement } from '@angular/core';
import { CoreTestingModule } from '@alfresco/adf-core';
import { FolderRulesService } from '../services/folder-rules.service';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { dummyRules } from '../mock/rules.mock';
import { By } from '@angular/platform-browser';
import { dummyNodeInfo } from '../mock/node.mock';
describe('ManageRulesSmartComponent', () => {
let fixture: ComponentFixture<ManageRulesSmartComponent>;
let component: ManageRulesSmartComponent;
let debugElement: DebugElement;
let folderRulesService: FolderRulesService;
beforeEach(
waitForAsync(() => {
const folderRulesServiceSpy = jasmine.createSpyObj('FolderRulesService', ['loadRules']);
TestBed.configureTestingModule({
imports: [CoreTestingModule, AcaFolderRulesModule],
providers: [
{ provide: FolderRulesService, useValue: folderRulesServiceSpy },
{ provide: ActivatedRoute, useValue: { params: of({ nodeId: 1 }) } }
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ManageRulesSmartComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement;
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
});
})
);
it('should display aca-rules-list and aca-rule-details', () => {
folderRulesService.folderInfo$ = of(dummyNodeInfo);
folderRulesService.rulesListing$ = of(dummyRules);
folderRulesService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
expect(folderRulesService.loadRules).toHaveBeenCalledOnceWith(component.nodeId);
const rules = debugElement.queryAll(By.css('.aca-rule'));
const ruleDetails = debugElement.queryAll(By.css('aca-rule-details'));
expect(rules.length).toBe(2, 'Unexpected number of aca-rule');
expect(ruleDetails.length).toBeTruthy('aca-rule-details was not rendered');
});
it('should only show adf-empty-content if provided node has no rules defined yet', () => {
folderRulesService.folderInfo$ = of(dummyNodeInfo);
folderRulesService.rulesListing$ = of([]);
folderRulesService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
const adfEmptyContent = debugElement.query(By.css('adf-empty-content'));
const rules = debugElement.query(By.css('.aca-rule'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(adfEmptyContent).toBeTruthy();
expect(rules).toBeFalsy();
expect(ruleDetails).toBeFalsy();
});
it('should only show aca-generic-error if the non-existing node was provided', () => {
folderRulesService.folderInfo$ = of(null);
folderRulesService.rulesListing$ = of([]);
folderRulesService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
const acaGenericError = debugElement.query(By.css('aca-generic-error'));
const rules = debugElement.query(By.css('.aca-rule'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(acaGenericError).toBeTruthy();
expect(rules).toBeFalsy();
expect(ruleDetails).toBeFalsy();
});
it('should only show progress bar while loading', () => {
folderRulesService.folderInfo$ = of(null);
folderRulesService.rulesListing$ = of([]);
folderRulesService.loading$ = of(true);
fixture.detectChanges();
expect(component).toBeTruthy();
const matProgressBar = debugElement.query(By.css('mat-progress-bar'));
const rules = debugElement.query(By.css('.aca-rule'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(matProgressBar).toBeTruthy();
expect(rules).toBeFalsy();
expect(ruleDetails).toBeFalsy();
});
});

View File

@@ -23,10 +23,52 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { FolderRulesService } from '../services/folder-rules.service';
import { Observable } from 'rxjs';
import { Rule } from '../model/rule.model';
import { ActivatedRoute } from '@angular/router';
import { NodeInfo } from '@alfresco/aca-shared/store';
import { tap } from 'rxjs/operators';
@Component({
selector: 'aca-manage-rules',
template: `<div>This is the Manage Rules component</div>`
templateUrl: 'manage-rules.smart-component.html',
styleUrls: ['manage-rules.smart-component.scss']
})
export class ManageRulesSmartComponent {}
export class ManageRulesSmartComponent implements OnInit {
rules$: Observable<Rule[]>;
isLoading$: Observable<boolean>;
folderInfo$: Observable<NodeInfo>;
selectedRule: Rule = null;
nodeId: string = null;
constructor(private location: Location, private folderRulesService: FolderRulesService, private route: ActivatedRoute) {}
ngOnInit(): void {
this.rules$ = this.folderRulesService.rulesListing$.pipe(
tap((rules) => {
if (!rules.includes(this.selectedRule)) {
this.selectedRule = rules[0];
}
})
);
this.isLoading$ = this.folderRulesService.loading$;
this.folderInfo$ = this.folderRulesService.folderInfo$;
this.route.params.subscribe((params) => {
this.nodeId = params.nodeId;
if (this.nodeId) {
this.folderRulesService.loadRules(this.nodeId);
}
});
}
goBack(): void {
this.location.back();
}
onRuleSelected(rule: Rule): void {
this.selectedRule = rule;
}
}

View File

@@ -0,0 +1,66 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export const dummyGetNodeResponse = {
entry: {
aspectNames: ['rule:rules', 'cm:titled', 'cm:auditable'],
createdAt: '2022-08-16T07:58:21.416+0000',
isFolder: true,
isFile: false,
createdByUser: {
id: 'username',
displayName: 'username'
},
modifiedAt: '2022-08-16T07:59:45.771+0000',
modifiedByUser: {
id: 'username',
displayName: 'username'
},
name: 'folder1',
id: '76659fe3-5f93-483d-948e-38b9e006cc94',
nodeType: 'cm:folder',
parentId: 'eb48d545-61f7-4ebd-861d-5fe5b072472f'
}
};
export const dummyNodeInfo = {
aspectNames: ['rule:rules', 'cm:titled', 'cm:auditable'],
createdAt: '2022-08-16T07:58:21.416+0000',
isFolder: true,
isFile: false,
createdByUser: {
id: 'username',
displayName: 'username'
},
modifiedAt: '2022-08-16T07:59:45.771+0000',
modifiedByUser: {
id: 'username',
displayName: 'username'
},
name: 'folder1',
id: '76659fe3-5f93-483d-948e-38b9e006cc94',
nodeType: 'cm:folder',
parentId: 'eb48d545-61f7-4ebd-861d-5fe5b072472f'
};

View File

@@ -1,3 +1,28 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Rule } from '../model/rule.model';
export const dummyResponse = {
@@ -12,12 +37,12 @@ export const dummyResponse = {
entries: [
{
entry: {
shared: false,
isShared: false,
cascade: false,
asynchronous: false,
name: 'rule1',
id: 'd388ed54-a522-410f-a158-6dbf5a833731',
triggers: ['INBOUND'],
triggers: ['inbound'],
actions: [
{
actionDefinitionId: 'copy',
@@ -33,12 +58,12 @@ export const dummyResponse = {
},
{
entry: {
shared: false,
isShared: false,
cascade: false,
asynchronous: false,
name: 'rule2',
id: 'e0e645ca-e6c0-47d4-9936-1a8872a6c30b',
triggers: ['INBOUND'],
triggers: ['inbound'],
actions: [
{
actionDefinitionId: 'move',
@@ -64,8 +89,8 @@ export const dummyRules: Rule[] = [
cascade: false,
asynchronous: false,
errorScript: '',
shared: false,
triggers: ['INBOUND'],
isShared: false,
triggers: ['inbound'],
conditions: null,
actions: [
{
@@ -86,8 +111,8 @@ export const dummyRules: Rule[] = [
cascade: false,
asynchronous: false,
errorScript: '',
shared: false,
triggers: ['INBOUND'],
isShared: false,
triggers: ['inbound'],
conditions: null,
actions: [
{

View File

@@ -34,8 +34,8 @@ export interface Rule {
cascade: boolean;
asynchronous: boolean;
errorScript: string;
shared: boolean;
triggers: ('INBOUND' | 'UPDATE' | 'OUTBOUND')[];
isShared: boolean;
triggers: ('inbound' | 'update' | 'outbound')[];
conditions: RuleCompositeCondition;
actions: RuleAction[];
}

View File

@@ -0,0 +1,7 @@
<div class="aca-rule" [class.selected]="isSelected" >
<div class="rule-info">
<span class="aca-rule-title">{{rule.name}}</span>
<p>{{rule.description}}</p>
</div>
</div>

View File

@@ -0,0 +1,27 @@
.aca-rule{
display: flex;
padding: 16px 24px;
border-radius: 12px;
margin-bottom: 8px;
cursor: pointer;
.aca-rule-title{
font-weight: 900;
font-size: 14px;
color: #212121;
line-height: 20px;
}
p{
margin: 6px 0 0 0;
color: rgba(33, 35, 40, 0.7);
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
}
}
.selected{
background: rgba(31, 116, 219, 0.24);
}

View File

@@ -0,0 +1,41 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RuleListItemUiComponent } from './rule-list-item.ui-component';
describe('RuleComponent', () => {
let component: RuleListItemUiComponent;
let fixture: ComponentFixture<RuleListItemUiComponent>;
beforeEach(() => {
fixture = TestBed.createComponent(RuleListItemUiComponent);
component = fixture.componentInstance;
});
it('should create the component', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input } from '@angular/core';
import { Rule } from '../../model/rule.model';
@Component({
selector: 'aca-rule',
templateUrl: 'rule-list-item.ui-component.html',
styleUrls: ['rule-list-item.ui-component.scss']
})
export class RuleListItemUiComponent {
@Input() rule: Rule;
@Input() isSelected: boolean;
}

View File

@@ -0,0 +1,3 @@
<div class="aca-rules-list" >
<aca-rule *ngFor="let rule of rules" [rule]="rule" (click)="onRuleClicked(rule)" [isSelected]="isSelected(rule)"></aca-rule>
</div>

View File

@@ -0,0 +1,3 @@
.aca-rules-list {
margin-right: 24px;
}

View File

@@ -0,0 +1,69 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RulesListUiComponent } from './rules-list.ui-component';
import { dummyRules } from '../mock/rules.mock';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { CoreTestingModule } from '@alfresco/adf-core';
import { AcaFolderRulesModule } from '@alfresco/aca-folder-rules';
describe('RulesListComponent', () => {
let component: RulesListUiComponent;
let fixture: ComponentFixture<RulesListUiComponent>;
let debugElement: DebugElement;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, AcaFolderRulesModule],
declarations: [RulesListUiComponent]
});
fixture = TestBed.createComponent(RulesListUiComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement;
});
it('should display the list of rules', () => {
expect(component).toBeTruthy();
component.rules = dummyRules;
fixture.detectChanges();
const rules = debugElement.queryAll(By.css('.aca-rule'));
expect(rules).toBeTruthy('Could not find rules');
expect(rules.length).toBe(2, 'Unexpected number of rules');
const rule = debugElement.query(By.css('.aca-rule:first-child'));
const title = rule.query(By.css('.aca-rule-title'));
const description = rule.query(By.css('p'));
expect(title.nativeElement.textContent).toBe(dummyRules[0].name);
expect(description.nativeElement.textContent).toBe(dummyRules[0].description);
});
});

View File

@@ -0,0 +1,54 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Rule } from '../model/rule.model';
@Component({
selector: 'aca-rules-list',
templateUrl: 'rules-list.ui-component.html',
styleUrls: ['rules-list.ui-component.scss']
})
export class RulesListUiComponent {
@Input()
rules: Rule[];
@Input()
selectedRule: Rule;
@Output()
ruleSelected = new EventEmitter<Rule>();
onRuleClicked(rule: Rule): void {
this.ruleSelected.emit(rule);
}
isSelected(rule): boolean {
if (this.selectedRule) {
return rule.id === this.selectedRule.id;
}
return false;
}
}

View File

@@ -30,39 +30,53 @@ import { of } from 'rxjs';
import { FolderRulesService } from './folder-rules.service';
import { Rule } from '../model/rule.model';
import { dummyResponse, dummyRules } from '../mock/rules.mock';
import { NodeInfo } from '@alfresco/aca-shared/store';
import { ContentApiService } from '@alfresco/aca-shared';
import { dummyGetNodeResponse, dummyNodeInfo } from '../mock/node.mock';
describe('FolderRulesService', () => {
let folderRulesService: FolderRulesService;
let contentApi: ContentApiService;
let rulesPromise: Promise<Partial<Rule>[]>;
let folderInfoPromise: Promise<NodeInfo>;
let rules: Partial<Rule>[];
let folderInfo: NodeInfo;
let apiCallSpy;
let getNodeSpy;
const nodeId = '';
const nodeId = '********-fake-node-****-********';
const ruleSetId = '-default-';
describe('loadRules', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [CoreTestingModule],
providers: [FolderRulesService]
providers: [FolderRulesService, ContentApiService]
});
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
contentApi = TestBed.inject<ContentApiService>(ContentApiService);
apiCallSpy = spyOn<any>(folderRulesService, 'apiCall').and.returnValue(of(dummyResponse) as any);
getNodeSpy = spyOn<any>(contentApi, 'getNode').and.returnValue(of(dummyGetNodeResponse) as any);
rulesPromise = folderRulesService.rulesListing$.pipe(take(2)).toPromise();
folderInfoPromise = folderRulesService.folderInfo$.pipe(take(2)).toPromise();
folderRulesService.loadRules(nodeId, ruleSetId);
rules = await rulesPromise;
folderInfo = await folderInfoPromise;
});
it('should format and set the data', async () => {
expect(rules).toBeTruthy('rulesListing$ is empty');
expect(folderInfo).toBeTruthy('folderInfo$ is empty');
expect(rules.length).toBe(2, 'rulesListing$ size is wrong');
expect(rules).toEqual(dummyRules, 'The list of rules is incorrectly formatted');
expect(folderInfo).toEqual(dummyNodeInfo, 'The node info is wrong');
expect(apiCallSpy).toHaveBeenCalledTimes(1);
expect(getNodeSpy).toHaveBeenCalledTimes(1);
});
});
});

View File

@@ -25,9 +25,11 @@
import { Injectable } from '@angular/core';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { BehaviorSubject, from, Observable } from 'rxjs';
import { finalize, map } from 'rxjs/operators';
import { BehaviorSubject, forkJoin, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { Rule } from '../model/rule.model';
import { ContentApiService } from '@alfresco/aca-shared';
import { NodeInfo } from '@alfresco/aca-shared/store';
@Injectable({
providedIn: 'root'
@@ -35,22 +37,47 @@ import { Rule } from '../model/rule.model';
export class FolderRulesService {
private rulesListingSource = new BehaviorSubject<Rule[]>([]);
rulesListing$: Observable<Rule[]> = this.rulesListingSource.asObservable();
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
folderInfo$: Observable<NodeInfo> = this.folderInfoSource.asObservable();
private loadingSource = new BehaviorSubject<boolean>(false);
loading$ = this.loadingSource.asObservable();
constructor(private apiService: AlfrescoApiService) {}
constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService) {}
loadRules(nodeId: string, ruleSetId: string = '-default-'): void {
from(this.apiCall(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', [{}, {}, {}, {}, {}, ['application/json'], ['application/json']]))
.pipe(
map((res) => this.formatRules(res)),
finalize(() => this.loadingSource.next(false))
)
.subscribe(
(res) => this.rulesListingSource.next(res),
(err) => this.rulesListingSource.error(err)
);
this.loadingSource.next(true);
forkJoin([
from(
this.apiCall(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', [{}, {}, {}, {}, {}, ['application/json'], ['application/json']])
).pipe(
map((res) => this.formatRules(res)),
catchError((error) => {
if (error.status === 404) {
return of([]);
}
return of(error);
})
),
this.contentApi.getNode(nodeId).pipe(
catchError((error) => {
if (error.status === 404) {
return of({ entry: null });
}
return of(error);
})
)
])
.pipe(finalize(() => this.loadingSource.next(false)))
.subscribe(
([rules, nodeInfo]) => {
this.rulesListingSource.next(rules);
this.folderInfoSource.next(nodeInfo.entry);
},
(error) => {
this.rulesListingSource.next([]);
this.folderInfoSource.next(error);
}
);
}
private apiCall(path: string, httpMethod: string, params?: any[]): Promise<any> {
@@ -70,8 +97,8 @@ export class FolderRulesService {
cascade: obj.cascade ?? false,
asynchronous: obj.asynchronous ?? false,
errorScript: obj.errorScript ?? '',
shared: obj.shared ?? false,
triggers: obj.triggers ?? ['INBOUND'],
isShared: obj.isShared ?? false,
triggers: obj.triggers ?? ['inbound'],
conditions: obj.conditions ?? null,
actions: obj.actions ?? []
};