[AAE-7856] Show variables in table

This commit is contained in:
Bartosz Sekula
2022-05-10 13:38:52 +02:00
parent 7c13a99ed7
commit 300c0e0c58
32 changed files with 528 additions and 52 deletions

View File

@@ -21,6 +21,7 @@ Defines column properties for DataTable, Tasklist, Document List and other compo
- [Column Template](#column-template)
- [Styling Techniques](#styling-techniques)
- [Using the copyContent option](#using-the-copycontent-option)
- [Exapmple of column customData](#example-of-column-customData)
- [See also](#see-also)
## Basic Usage
@@ -52,6 +53,7 @@ Defines column properties for DataTable, Tasklist, Document List and other compo
| formatTooltip | `Function` | | Custom tooltip formatter function. |
| key | `string` | | Data source key. Can be either a column/property key like `title` or a property path like `createdBy.name`. |
| sortable | `boolean` | true | Toggles ability to sort by this column, for example by clicking the column header. |
| customData | `Generic` | any | Any feature specific data |
| draggable | `boolean` | false | Toggles drag and drop for header column. |
| isHidden | `boolean` | false | Hides columns |
| sortingKey | `string` | | When using server side sorting the column used by the api call where the sorting will be performed |
@@ -351,6 +353,35 @@ HTML `<data-column>` element example:
</adf-tasklist>
```
### Example of column customData
If you would like to pass any custom data related to your specific feature, you can use customData
HTML `<data-column>` element example:
```html
<data-column [customData]="MyCustomData" key="id" title="Id"></data-column>
```
You can use generic type for `DataColumn` in order to get intellisense working e.g.
```ts
const dataColumn: DataColumn<{ shouldPerformActionIfDisplayed: boolean }> = {
...
customData: { shouldPerformActionIfDisplayed: true }
}
// We should get proper types
consol.log(dataColumn.customData.shouldPerformActionIfDisplayed);
// Now we can use this data in our feature e.g.
const shouldPerformAction = this.columns
.filter(column => column.isHidden)
.some(column => column.customData?.shouldPerformActionIfDisplayed === true);
if (shouldPerformAction) { /* action */}
```
## See also
- [Document list component](../../content-services/components/document-list.component.md)

View File

@@ -70,6 +70,10 @@ export class ShareDataTableAdapter implements DataTableAdapter {
this.allowDropFiles = allowDropFiles;
}
getColumnType(_row: DataRow, col: DataColumn): string {
return col.type;
}
getRows(): Array<DataRow> {
return this.rows;
}

View File

@@ -34,6 +34,10 @@ export class DataColumnComponent implements OnInit {
@Input()
key: string;
/** You can specify any custom data which can be used by any specific feature */
@Input()
customData: any;
/** Value type for the column. Possible settings are 'text', 'image',
* 'date', 'fileSize', 'location', and 'json'.
*/
@@ -52,6 +56,10 @@ export class DataColumnComponent implements OnInit {
@Input()
draggable: boolean = false;
/* Hide column */
@Input()
isHidden: boolean = false;
/** Display title of the column, typically used for column headers. You can use the
* i18n resource key to get it translated automatically.
*/

View File

@@ -24,20 +24,22 @@
[placeholder]='"ADF-DATATABLE.COLUMNS_SELECTOR.SEARCH" | translate'>
</div>
<ng-container *ngFor="let column of columnItems">
<div
*ngIf="(column.title | translate | filterString:searchQuery) as translatedTitle"
class="adf-columns-selector-list-item-container">
<mat-checkbox
color="primary"
class="adf-columns-selector-column-checkbox"
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
[checked]="!column.isHidden"
(change)="changeColumnVisibility(column)">
<div class="adf-columns-selector-list-content">{{translatedTitle}}</div>
</mat-checkbox>
</div>
</ng-container>
<div class="adf-columns-selector-list-container">
<ng-container *ngFor="let column of columnItems">
<div
*ngIf="(column.title | translate | filterString:searchQuery) as translatedTitle"
class="adf-columns-selector-list-item">
<mat-checkbox
color="primary"
class="adf-columns-selector-column-checkbox"
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
[checked]="!column.isHidden"
(change)="changeColumnVisibility(column)">
<div class="adf-columns-selector-list-content">{{translatedTitle}}</div>
</mat-checkbox>
</div>
</ng-container>
</div>
<mat-divider class="adf-columns-selector-divider"></mat-divider>

View File

@@ -25,7 +25,13 @@ $adf-columns-selector-space: 12px;
font-size: var(--theme-body-1-font-size);
}
&-list-item-container {
&-list-container {
max-height: 350px;
overflow-x: hidden;
overflow-y: auto;
}
&-list-item {
margin-top: 10px;
&:hover {

View File

@@ -194,7 +194,7 @@
[adf-context-menu-enabled]="contextMenu"
adf-drop-zone dropTarget="cell" [dropColumn]="col" [dropRow]="row">
<div *ngIf="!col.template" class="adf-datatable-cell-container">
<ng-container [ngSwitch]="col.type">
<ng-container [ngSwitch]="data.getColumnType(row, col)">
<div *ngSwitchCase="'image'" class="adf-cell-value">
<mat-icon *ngIf="isIconValue(row, col); else no_iconvalue">{{ asIconValue(row, col) }}
</mat-icon>
@@ -204,12 +204,12 @@
</mat-icon>
<ng-template #no_selected_row>
<img class="adf-datatable-center-img-ie"
[attr.aria-label]=" (data.getValue(row, col) | fileType) === 'disable' ?
[attr.aria-label]="(data.getValue(row, col) | fileType) === 'disable' ?
('ADF-DATATABLE.ACCESSIBILITY.ICON_DISABLED' | translate) :
'ADF-DATATABLE.ACCESSIBILITY.ICON_TEXT' | translate:{
type: 'ADF-DATATABLE.FILE_TYPE.' + (data.getValue(row, col) | fileType | uppercase) | translate
}"
[attr.alt]=" (data.getValue(row, col) | fileType) === 'disable' ?
[attr.alt]="(data.getValue(row, col) | fileType) === 'disable' ?
('ADF-DATATABLE.ACCESSIBILITY.ICON_DISABLED' | translate) :
'ADF-DATATABLE.ACCESSIBILITY.ICON_TEXT' | translate:{
type: 'ADF-DATATABLE.FILE_TYPE.' + (data.getValue(row, col) | fileType | uppercase) | translate

View File

@@ -29,7 +29,7 @@ export interface DataColumnTypes {
export type DataColumnType = keyof DataColumnTypes;
export interface DataColumn {
export interface DataColumn<T = unknown> {
id?: string;
key: string;
type: DataColumnType;
@@ -47,4 +47,5 @@ export interface DataColumn {
header?: TemplateRef<any>;
draggable?: boolean;
isHidden?: boolean;
customData?: T;
}

View File

@@ -24,7 +24,7 @@ import { ObjectDataColumn } from './object-datacolumn.model';
@Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class DataTableSchema {
export abstract class DataTableSchema<T = unknown> {
@ContentChild(DataColumnListComponent)
columnList: DataColumnListComponent;
@@ -33,7 +33,7 @@ export abstract class DataTableSchema {
@Input()
presetColumn: string;
columns: any;
columns: DataColumn<T>[];
protected columnsOrder: string[] | undefined;
protected columnsOrderedByKey: string = 'id';
@@ -91,7 +91,7 @@ export abstract class DataTableSchema {
return customSchemaColumns;
}
public getSchemaFromHtml(columnList: DataColumnListComponent): any {
public getSchemaFromHtml(columnList: DataColumnListComponent): DataColumn[] {
let schema = [];
if (columnList && columnList.columns && columnList.columns.length > 0) {
schema = columnList.columns.map((c) => c as DataColumn);

View File

@@ -29,6 +29,7 @@ export interface DataTableAdapter {
getColumns(): Array<DataColumn>;
setColumns(columns: Array<DataColumn>): void;
getValue(row: DataRow, col: DataColumn, resolverFn?: (_row: DataRow, _col: DataColumn) => any): any;
getColumnType(row: DataRow, col: DataColumn): string;
getSorting(): DataSorting;
setSorting(sorting: DataSorting): void;
sort(key?: string, direction?: string): void;

View File

@@ -19,7 +19,7 @@ import { TemplateRef } from '@angular/core';
import { DataColumn, DataColumnType } from './data-column.model';
// Simple implementation of the DataColumn interface.
export class ObjectDataColumn implements DataColumn {
export class ObjectDataColumn<T = unknown> implements DataColumn<T> {
id?: string;
key: string;
type: DataColumnType;
@@ -35,6 +35,7 @@ export class ObjectDataColumn implements DataColumn {
header?: TemplateRef<any>;
draggable: boolean;
isHidden: boolean;
customData?: T;
constructor(input: any) {
this.id = input.id ?? '';
@@ -52,5 +53,6 @@ export class ObjectDataColumn implements DataColumn {
this.header = input.header;
this.draggable = input.draggable ?? false;
this.isHidden = input.isHidden ?? false;
this.customData = input.customData;
}
}

View File

@@ -77,6 +77,10 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
this.rowsChanged = new Subject<Array<DataRow>>();
}
getColumnType(_row: DataRow, col: DataColumn): string {
return col.type;
}
getRows(): Array<DataRow> {
return this._rows;
}

View File

@@ -0,0 +1,22 @@
import { DataColumn } from '../datatable/data/data-column.model';
export const getDataColumnMock = <T = unknown>(column: Partial<DataColumn<T>> = {}): DataColumn<T> => ({
id: 'columnId',
key: 'key',
type: 'text',
format: 'format',
sortable: false,
title: 'title',
srTitle: 'srTitle',
cssClass: 'cssClass',
template: undefined,
copyContent: false,
editable: false,
focus: false,
sortingKey: 'sortingKey',
header: undefined,
draggable: false,
isHidden: false,
customData: undefined,
...column
});

View File

@@ -41,3 +41,4 @@ export * from './identity-group.mock';
export * from './identity-user.mock';
export * from './identity-group.service.mock';
export * from './identity-user.service.mock';
export * from './data-column.mock';

View File

@@ -0,0 +1,4 @@
// eslint-disable-next-line no-shadow
export enum ColumnDataType {
processVariableColumn = 'process-variable-column'
}

View File

@@ -15,6 +15,8 @@
* limitations under the License.
*/
import { ProcessVariableDefinition } from './variable-definition';
export class ProcessDefinitionCloud {
id: string;
appName: string;
@@ -25,6 +27,7 @@ export class ProcessDefinitionCloud {
name: string;
category: string;
description: string;
variableDefinitions?: ProcessVariableDefinition[];
constructor(obj?: any) {
this.id = obj && obj.id || null;
@@ -36,5 +39,6 @@ export class ProcessDefinitionCloud {
this.appVersion = obj && obj.appVersion || 0;
this.category = obj && obj?.category || '';
this.description = obj && obj?.description || '';
this.variableDefinitions = obj?.variableDefinitions ?? [];
}
}

View File

@@ -0,0 +1,33 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface ProcessInstanceVariable {
id: number;
variableDefinitionId: string;
value: string;
appName: string;
createTime: string;
lastUpdatedTime: string;
markedAsDeleted: boolean;
name: string;
processInstanceId: string;
serviceFullName: string;
serviceName: string;
serviceVersion: string;
taskVariable: boolean;
type: string;
}

View File

@@ -0,0 +1,25 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface ProcessVariableDefinition {
id: string;
name: string;
type: string;
required: boolean;
display: boolean;
displayName?: string;
}

View File

@@ -1,6 +1,7 @@
<adf-datatable #dataTable
[rows]="rows"
[columns]="columns"
[data]="dataAdapter"
[stickyHeader]="stickyHeader"
[loading]="isLoading"
[sorting]="formattedSorting"

View File

@@ -22,6 +22,7 @@ import {
ColumnsSelectorComponent,
DataColumn,
DataRowEvent,
getDataColumnMock,
ObjectDataRow,
setupTestBed
} from '@alfresco/adf-core';
@@ -33,6 +34,10 @@ import { shareReplay, skip } from 'rxjs/operators';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
import { ColumnDataType } from '../../../models/column-data-type.model';
@Component({
template: `
@@ -71,6 +76,9 @@ describe('ProcessListCloudComponent', () => {
let fixture: ComponentFixture<ProcessListCloudComponent>;
let appConfig: AppConfigService;
let processListCloudService: ProcessListCloudService;
let preferencesService: LocalPreferenceCloudService;
const fakeCustomSchemaName = 'fakeCustomSchema';
const schemaWithVariable = 'schemaWithVariableId';
setupTestBed({
imports: [
@@ -82,12 +90,13 @@ describe('ProcessListCloudComponent', () => {
beforeEach(() => {
appConfig = TestBed.inject(AppConfigService);
processListCloudService = TestBed.inject(ProcessListCloudService);
preferencesService = TestBed.inject<LocalPreferenceCloudService>(PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN);
fixture = TestBed.createComponent(ProcessListCloudComponent);
component = fixture.componentInstance;
appConfig.config = Object.assign(appConfig.config, {
'adf-cloud-process-list': {
presets: {
fakeCustomSchema: [
[fakeCustomSchemaName]: [
{
key: 'fakeName',
type: 'text',
@@ -100,6 +109,16 @@ describe('ProcessListCloudComponent', () => {
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.TASK_FAKE',
sortable: true
}
],
[schemaWithVariable]: [
getDataColumnMock(),
getDataColumnMock({
id: 'variableColumnId',
customData: {
assignedVariableDefinitionIds: ['variableDefinitionId'],
columnType: ColumnDataType.processVariableColumn
}
})
]
}
}
@@ -108,11 +127,12 @@ describe('ProcessListCloudComponent', () => {
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
});
afterEach(() => fixture.destroy());
afterEach(() => {
fixture.destroy();
});
it('should use the default schemaColumn', () => {
appConfig.config = Object.assign(appConfig.config, { 'adf-cloud-process-list': processListSchemaMock });
component.ngAfterContentInit();
fixture.detectChanges();
expect(component.columns).toBeDefined();
@@ -164,6 +184,7 @@ describe('ProcessListCloudComponent', () => {
it('should the payload contain the appVersion if it is defined', () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
component.appVersion = 1;
component.ngAfterContentInit();
component.reload();
expect(component.requestNode.appVersion).toEqual('1');
@@ -172,6 +193,7 @@ describe('ProcessListCloudComponent', () => {
it('should the payload contain all the app versions joined by a comma separator', () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
component.appVersion = [1, 2, 3];
component.ngAfterContentInit();
component.reload();
expect(component.requestNode.appVersion).toEqual('1,2,3');
@@ -180,20 +202,21 @@ describe('ProcessListCloudComponent', () => {
it('should the payload NOT contain any app version when appVersion does not have a value', () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
component.appVersion = undefined;
component.ngAfterContentInit();
component.reload();
expect(component.requestNode.appVersion).toEqual('');
});
it('should use the custom schemaColumn from app.config.json', () => {
component.presetColumn = 'fakeCustomSchema';
component.presetColumn = fakeCustomSchemaName;
component.ngAfterContentInit();
fixture.detectChanges();
expect(component.columns).toEqual(fakeCustomSchema);
});
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
component.presetColumn = 'fakeCustomSchema';
component.presetColumn = fakeCustomSchemaName;
fixture.detectChanges();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(2);
@@ -223,6 +246,7 @@ describe('ProcessListCloudComponent', () => {
done();
});
component.appName = appName.currentValue;
component.ngAfterContentInit();
component.ngOnChanges({ appName });
fixture.detectChanges();
});
@@ -244,6 +268,7 @@ describe('ProcessListCloudComponent', () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngAfterContentInit();
component.ngOnChanges({ appName });
fixture.detectChanges();
@@ -285,6 +310,41 @@ describe('ProcessListCloudComponent', () => {
expect(displayedColumns.length).toBe(2, 'only column with isHidden set to false and action column should be shown');
});
it('should NOT request process variable if columns for process variables are not displayed', () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
spyOn(preferencesService, 'getPreferences').and.returnValue(of({
list: {
entries: []
}
}));
component.ngAfterContentInit();
component.reload();
expect(component.requestNode.variableDefinitions).not.toBeDefined();
});
it('should request process variable if column for process variable is displayed', () => {
component.presetColumn = schemaWithVariable;
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
spyOn(preferencesService, 'getPreferences').and.returnValue(of({
list: {
entries: [{
entry: {
key: ProcessListCloudPreferences.columnsVisibility,
value: '{"variableColumnId":true, "2":true}'
}
}]
}
}));
component.ngAfterContentInit();
component.reload();
expect(component.requestNode.variableDefinitions).toEqual(['variableDefinitionId']);
});
it('should reload tasks when reload() is called', (done) => {
component.appName = 'fake';
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));

View File

@@ -21,14 +21,17 @@ import { DataTableSchema, PaginatedComponent,
UserPreferencesService, PaginationModel,
UserPreferenceValues, DataRowEvent, CustomLoadingContentTemplateDirective, DataCellEvent, DataRowActionEvent, DataTableComponent, DataColumn } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../services/process-list-cloud.service';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { BehaviorSubject, of } from 'rxjs';
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
import { map, take } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
import { ColumnDataType } from '../../../models/column-data-type.model';
import { ProcessListDatatableAdapter } from '../datatable/process-list-datatable-adapter';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
const PRESET_KEY = 'adf-cloud-process-list.presets';
@@ -38,7 +41,7 @@ const PRESET_KEY = 'adf-cloud-process-list.presets';
styleUrls: ['./process-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent {
export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataColumnCustomData> implements OnChanges, AfterContentInit, PaginatedComponent {
@ViewChild(DataTableComponent)
dataTable: DataTableComponent;
@@ -201,6 +204,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
rows: any[] = [];
formattedSorting: any[];
requestNode: ProcessQueryCloudRequestModel;
dataAdapter: ProcessListDatatableAdapter;
private defaultSorting = { key: 'startDate', direction: 'desc' };
@@ -231,7 +235,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
return {
columnsOrder: columnsOrder ? JSON.parse(columnsOrder.entry.value) : undefined,
columnsVisibility: columnsVisibility ? JSON.parse(columnsVisibility.entry.value) : undefined
columnsVisibility: columnsVisibility ? JSON.parse(columnsVisibility.entry.value) : this.columnsVisibility
};
}))
)
@@ -262,24 +266,29 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
}
reload() {
this.requestNode = this.createRequestNode();
if (this.requestNode.appName || this.requestNode.appName === '') {
this.load(this.requestNode);
if (this.appName || this.appName === '') {
this.load();
} else {
this.rows = [];
}
}
private load(requestNode: ProcessQueryCloudRequestModel) {
private load() {
this.isLoading = true;
combineLatest([
this.processListCloudService.getProcessByRequest(requestNode),
this.isColumnSchemaCreated$
]).pipe(
take(1)
).subscribe(([processes]) => {
this.rows = processes.list.entries;
this.isColumnSchemaCreated$.pipe(
take(1),
switchMap(() => of(this.createRequestNode())),
tap((requestNode) => this.requestNode = requestNode),
switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode))
).subscribe((processes) => {
this.rows = this.processListCloudService.createRowsViewModel(
processes.list.entries,
this.columns
);
this.dataAdapter = new ProcessListDatatableAdapter(this.rows, this.columns);
this.success.emit(processes);
this.isLoading = false;
this.pagination.next(processes.list.pagination);
@@ -359,6 +368,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
}, {});
this.createColumns();
this.reload();
if (this.appName) {
this.cloudPreferenceService.updatePreference(
@@ -427,8 +437,10 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
suspendedFrom: this.suspendedFrom,
suspendedTo: this.suspendedTo,
completedDate: this.completedDate,
sorting: this.sorting
sorting: this.sorting,
variableDefinitions: this.getRequestNodeVariableIds()
};
return new ProcessQueryCloudRequestModel(requestNode);
}
@@ -454,4 +466,16 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
isValidSorting(sorting: ProcessListCloudSortingModel[]) {
return sorting.length && sorting[0].orderBy && sorting[0].direction;
}
private getRequestNodeVariableIds(): string[] | undefined {
const displayedVariableColumns = this.columns
.filter(column =>
column.customData?.columnType === ColumnDataType.processVariableColumn &&
column.isHidden !== true
)
.map(column => column.customData.assignedVariableDefinitionIds)
.reduce((allIds, ids) => [...ids, ...allIds], []);
return displayedVariableColumns.length ? displayedVariableColumns : undefined;
}
}

View File

@@ -0,0 +1,53 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DataColumn, DataRow, getDataColumnMock } from '@alfresco/adf-core';
import { ColumnDataType } from '../../../models/column-data-type.model';
import { getProcessInstanceVariableMock } from '../mock/process-instance-variable.mock';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
import { ProcessInstanceCloudListViewModel } from '../models/perocess-instance-cloud-view.model';
import { ProcessListDatatableAdapter } from './process-list-datatable-adapter';
describe('ProcessListDatatableAdapter', () => {
it('should get proepr type for column', () => {
const viewModel: ProcessInstanceCloudListViewModel = {
id: '1',
variablesMap: {
columnDisplayName1: getProcessInstanceVariableMock({ type: 'number' })
}
};
const row: DataRow = {
getValue: () => {},
hasValue: () => true,
isSelected: false,
obj: viewModel
};
const column: DataColumn<ProcessListDataColumnCustomData> = getDataColumnMock({
title: 'columnDisplayName1',
customData: {
assignedVariableDefinitionIds: ['1'],
columnType: ColumnDataType.processVariableColumn
}
});
const adapter = new ProcessListDatatableAdapter([], []);
expect(adapter.getColumnType(row, column)).toBe('number');
});
});

View File

@@ -0,0 +1,23 @@
import { DataColumn, DataRow, ObjectDataTableAdapter } from '@alfresco/adf-core';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
import { ColumnDataType } from '../../../models/column-data-type.model';
import { ProcessInstanceCloudListViewModel } from '../models/perocess-instance-cloud-view.model';
export class ProcessListDatatableAdapter extends ObjectDataTableAdapter {
constructor(
data: ProcessInstanceCloudListViewModel[],
schema: DataColumn<ProcessListDataColumnCustomData>[]
) {
super(data, schema);
}
getColumnType(row: DataRow, col: DataColumn<ProcessListDataColumnCustomData>): string {
if (col.customData?.columnType === ColumnDataType.processVariableColumn) {
const variableDisplayName = col.title;
const columnType = row.obj.variablesMap?.[variableDisplayName]?.type;
return columnType ?? 'text';
}
return super.getColumnType(row, col);
}
}

View File

@@ -0,0 +1,19 @@
import { ProcessInstanceVariable } from '../../../models/process-instance-variable.model';
export const getProcessInstanceVariableMock = (variable: Partial<ProcessInstanceVariable> = {}): ProcessInstanceVariable => ({
id: 1,
variableDefinitionId: 'variableDefinitionId',
value: 'value',
appName: 'appName',
createTime: 'createTime',
lastUpdatedTime: 'lastUpdatedTime',
markedAsDeleted: false,
name: 'name',
processInstanceId: 'processInstanceId',
serviceFullName: 'serviceFullName',
serviceName: 'serviceName',
serviceVersion: 'serviceVersion',
taskVariable: false,
type: 'text',
...variable
});

View File

@@ -16,6 +16,7 @@
*/
import { ObjectDataColumn } from '@alfresco/adf-core';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
export const fakeProcessCloudList = {
list: {
@@ -34,7 +35,8 @@ export const fakeProcessCloudList = {
status: 'RUNNING',
lastModified: 1540381146276,
lastModifiedTo: null,
lastModifiedFrom: null
lastModifiedFrom: null,
variables: [{ id: 'variableId', value: 'variableValue'}]
}
},
{
@@ -84,13 +86,13 @@ export const fakeProcessCloudList = {
export const fakeCustomSchema =
[
new ObjectDataColumn({
new ObjectDataColumn<ProcessListDataColumnCustomData>({
key: 'fakeName',
type: 'text',
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.FAKE',
sortable: true
}),
new ObjectDataColumn({
new ObjectDataColumn<ProcessListDataColumnCustomData>({
key: 'fakeTaskName',
type: 'text',
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.TASK_FAKE',

View File

@@ -0,0 +1,4 @@
export interface ProcessListDataColumnCustomData {
assignedVariableDefinitionIds: string[];
columnType: string;
}

View File

@@ -0,0 +1,25 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ProcessInstanceVariable } from '../../../models/process-instance-variable.model';
import { ProcessInstanceCloud } from '../../start-process/models/process-instance-cloud.model';
export interface ProcessInstanceCloudListViewModel extends ProcessInstanceCloud {
variablesMap?: {
[variableDisplayName: string]: ProcessInstanceVariable;
};
}

View File

@@ -42,6 +42,8 @@ export class ProcessQueryCloudRequestModel {
maxItems: number;
skipCount: number;
sorting?: ProcessListCloudSortingModel[];
variableDefinitions?: string[];
constructor(obj?: any) {
if (obj) {
this.appName = obj.appName;
@@ -68,6 +70,7 @@ export class ProcessQueryCloudRequestModel {
this.maxItems = obj.maxItems;
this.skipCount = obj.skipCount;
this.sorting = obj.sorting;
this.variableDefinitions = obj.variableDefinitions;
}
}
}

View File

@@ -15,10 +15,16 @@
* limitations under the License.
*/
import { fakeAsync, TestBed } from '@angular/core/testing';
import { setupTestBed, AlfrescoApiService } from '@alfresco/adf-core';
import { setupTestBed, AlfrescoApiService, getDataColumnMock } from '@alfresco/adf-core';
import { ProcessListCloudService } from './process-list-cloud.service';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { ProcessInstanceVariable } from '../../../models/process-instance-variable.model';
import { ProcessInstanceCloudListViewModel } from '../models/perocess-instance-cloud-view.model';
import { ProcessInstanceCloud } from '../../public-api';
import { getProcessInstanceVariableMock } from '../mock/process-instance-variable.mock';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
import { ColumnDataType } from '../../../models/column-data-type.model';
describe('ProcessListCloudService', () => {
let service: ProcessListCloudService;
@@ -98,4 +104,35 @@ describe('ProcessListCloudService', () => {
}
);
});
it('should map to view model', () => {
const processInstanceVariable: ProcessInstanceVariable = getProcessInstanceVariableMock({
variableDefinitionId: '5c75b259-dc59-11ec-aa89-fed162b97957'
});
const columnTitle = 'columnTitle';
const column = getDataColumnMock<ProcessListDataColumnCustomData>({
title: columnTitle,
customData: {
assignedVariableDefinitionIds: ['5c75b259-dc59-11ec-aa89-fed162b97957'],
columnType: ColumnDataType.processVariableColumn
}
});
const processInstance: ProcessInstanceCloud = {
id: 'id',
variables: [processInstanceVariable]
};
const expectedViewModel: ProcessInstanceCloudListViewModel = {
...processInstance,
variablesMap: {
[columnTitle]: processInstanceVariable
}
};
const viewModel = service.createRowsViewModel([processInstance], [column]);
expect(viewModel).toEqual([expectedViewModel]);
});
});

View File

@@ -15,12 +15,15 @@
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
import { AlfrescoApiService, AppConfigService, DataColumn, DataColumnType, LogService } from '@alfresco/adf-core';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { Observable, throwError } from 'rxjs';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
import { BaseCloudService } from '../../../services/base-cloud.service';
import { map } from 'rxjs/operators';
import { ProcessInstanceCloudListViewModel } from '../models/perocess-instance-cloud-view.model';
import { ProcessInstanceCloud } from '../../start-process/models/process-instance-cloud.model';
import { ProcessListDataColumnCustomData } from '../models/data-column-custom-data';
@Injectable({ providedIn: 'root' })
export class ProcessListCloudService extends BaseCloudService {
@@ -62,6 +65,51 @@ export class ProcessListCloudService extends BaseCloudService {
}
}
createRowsViewModel(
processes: ProcessInstanceCloud[] = [],
columnsSchema: DataColumn<ProcessListDataColumnCustomData>[]
): ProcessInstanceCloudListViewModel[] {
const columnsByVariableId = columnsSchema
.filter(column => !!column.customData)
.reduce<{ [variableId: string]: string }>((columnsByVariable, column) => {
const columnTitle = column.title;
const variableIds = column.customData.assignedVariableDefinitionIds;
variableIds.forEach((variableId) => {
columnsByVariable[variableId] = columnTitle;
});
return columnsByVariable;
}, {});
const rowsViewModel = processes.map((process) => {
if (!process.variables?.length) {
return process;
}
const variablesMap = (process.variables ?? []).reduce((variableAccumulator, variable) => {
const processVariableDefinitionId = variable.variableDefinitionId;
const column = columnsByVariableId[processVariableDefinitionId];
if (column) {
variableAccumulator[column] = {
...variable,
type: this.mapProcessVariableTypes(variable.type)
};
}
return variableAccumulator;
}, {});
return {
...process,
variablesMap
};
});
return rowsViewModel;
}
protected isPropertyValueValid(requestNode: any, property: string): boolean {
return requestNode[property] !== '' && requestNode[property] !== null && requestNode[property] !== undefined;
}
@@ -73,7 +121,7 @@ export class ProcessListCloudService extends BaseCloudService {
if (requestNode.hasOwnProperty(property) &&
!this.isExcludedField(property) &&
this.isPropertyValueValid(requestNode, property)) {
queryParam[property] = requestNode[property];
queryParam[property] = this.getQueryParamValueFromRequestNode(requestNode, property as keyof ProcessQueryCloudRequestModel);
}
}
@@ -84,6 +132,17 @@ export class ProcessListCloudService extends BaseCloudService {
return queryParam;
}
private getQueryParamValueFromRequestNode(
requestNode: ProcessQueryCloudRequestModel,
property: keyof ProcessQueryCloudRequestModel
) {
if (property === 'variableDefinitions' && requestNode[property]?.length > 0) {
return `${requestNode[property].map(variableId => variableId).join(',')}`;
}
return requestNode[property];
}
protected buildFilterForAllStatus(): string[] {
return ['RUNNING', 'SUSPENDED', 'CANCELLED', 'COMPLETED'];
}
@@ -105,4 +164,18 @@ export class ProcessListCloudService extends BaseCloudService {
}
return encodeURI(finalSorting);
}
private mapProcessVariableTypes(variableType: string): DataColumnType {
switch (variableType) {
case 'boolean':
case 'integer':
case 'string':
return 'text';
case 'date':
case 'datetime':
return 'date';
default:
return 'text';
}
}
}

View File

@@ -15,6 +15,7 @@
* limitations under the License.
*/
import { ProcessInstanceVariable } from '../../../models/process-instance-variable.model';
export interface ProcessInstanceCloud {
appName?: string;
id?: string;
@@ -28,4 +29,5 @@ export interface ProcessInstanceCloud {
processDefinitionId?: string;
processDefinitionKey?: string;
processDefinitionName?: string;
variables?: ProcessInstanceVariable[];
}

View File

@@ -42,11 +42,11 @@ export class StartProcessCloudService extends BaseCloudService {
* @param appName Name of the target app
* @returns Array of process definitions
*/
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]> {
getProcessDefinitions(appName: string, queryParams?: { include: 'variables' }): Observable<ProcessDefinitionCloud[]> {
if (appName || appName === '') {
const url = `${this.getBasePath(appName)}/rb/v1/process-definitions`;
return this.get(url).pipe(
return this.get(url, queryParams).pipe(
map((res: any) => res.list.entries.map((processDefs) => new ProcessDefinitionCloud(processDefs.entry)))
);
} else {

View File

@@ -32,3 +32,5 @@ export * from './lib/models/application-version.model';
export * from './lib/models/engine-event-cloud.model';
export * from './lib/models/filter-cloud-model';
export * from './lib/models/task-list-sorting.model';
export * from './lib/models/column-data-type.model';
export * from './lib/models/process-instance-variable.model';