Skip to content

Commit

Permalink
Merge branch 'main' into mboulais/O2B-1393/use-certificate-when-handl…
Browse files Browse the repository at this point in the history
…ing-lost-runs-and-envs
  • Loading branch information
martinboulais authored Jan 23, 2025
2 parents a11cbdd + 1a9f97b commit 3fc88c9
Show file tree
Hide file tree
Showing 19 changed files with 282 additions and 152 deletions.
18 changes: 17 additions & 1 deletion lib/public/components/Filters/common/FilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Observable } from '/js/src/index.js';

/**
* Model storing the state of a given filter
*
* @abstract
*/
export class FilterModel extends Observable {
Expand Down Expand Up @@ -49,7 +50,7 @@ export class FilterModel extends Observable {
/**
* Returns the normalized value of the filter, that can be used as URL parameter
*
* @return {string|number|object|array|null} the normalized value
* @return {string|number|object|string[]|number[]|null} the normalized value
* @abstract
*/
get normalized() {
Expand All @@ -64,4 +65,19 @@ export class FilterModel extends Observable {
get visualChange$() {
return this._visualChange$;
}

/**
* Utility function to register a filter model as sub-filter model
*
* TODO for now this function simply propagate observable notifications, in the future this should register submodels with a key and allow
* automated normalization (recursive filter model)
*
* @param {object} subModel the submodel to register
* @return {void}
* @protected
*/
_addSubmodel(subModel) {
subModel.bubbleTo(this);
subModel?.visualChange$.bubbleTo(this._visualChange$);
}
}
100 changes: 100 additions & 0 deletions lib/public/components/Filters/common/FilteringModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { Observable } from '/js/src/index.js';

/**
* Model representing a filtering system, including filter inputs visibility, filters values and so on
*/
export class FilteringModel extends Observable {
/**
* Constructor
*
* @param {Object<string, FilterModel>} filters the filters with their label and model
*/
constructor(filters) {
super();

this._visualChange$ = new Observable();

this._filters = filters;
this._filterModels = Object.values(filters);
for (const model of this._filterModels) {
model.bubbleTo(this);
model.visualChange$?.bubbleTo(this._visualChange$);
}
}

/**
* Reset the filters
*
* @return {void}
*/
reset() {
for (const model of this._filterModels) {
model.reset();
}
}

/**
* Returns the normalized value of all the filters, without null values
*
* @return {object} the normalized values
*/
get normalized() {
const ret = {};
for (const [filterKey, filter] of Object.entries(this._filters)) {
if (filter && !filter.isEmpty) {
ret[filterKey] = filter.normalized;
}
}
return ret;
}

/**
* States if there is currently at least one filter active
*
* @return {boolean} true if at least one filter is active
*/
isAnyFilterActive() {
for (const model of this._filterModels) {
if (!model.isEmpty) {
return true;
}
}
return false;
}

/**
* Returns the observable notified any time there is a visual change which has no impact on the actual filtering
*
* @return {Observable} the filters visibility observable
*/
get visualChange$() {
return this._visualChange$;
}

/**
* Return the filter model for a given key
*
* @param {string} key the key of the filtering model
* @return {FilterModel} the filtering model
*/
get(key) {
if (!(key in this._filters)) {
throw new Error(`No filter found with key ${key}`);
}

return this._filters[key];
}
}
48 changes: 24 additions & 24 deletions lib/public/components/Filters/common/TagFilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/
import { Observable } from '/js/src/index.js';
import { CombinationOperatorChoiceModel } from './CombinationOperatorChoiceModel.js';
import { TagSelectionDropdownModel } from '../../tag/TagSelectionDropdownModel.js';
import { FilterModel } from './FilterModel.js';

/**
* Model to handle the state of a tags filter
*/
export class TagFilterModel extends Observable {
export class TagFilterModel extends FilterModel {
/**
* Constructor
*
Expand All @@ -27,29 +27,38 @@ export class TagFilterModel extends Observable {
constructor(operators) {
super();
this._selectionModel = new TagSelectionDropdownModel({ includeArchived: true });
this._selectionModel.bubbleTo(this);
this._addSubmodel(this._selectionModel);

this._combinationOperatorModel = new CombinationOperatorChoiceModel(operators);
this._combinationOperatorModel.bubbleTo(this);
this._addSubmodel(this._combinationOperatorModel);
}

// eslint-disable-next-line valid-jsdoc
/**
* States if the filter has no tags selected
*
* @return {boolean} true if no tags are selected
* @inheritDoc
*/
isEmpty() {
return this.selected.length === 0;
reset() {
this._selectionModel.reset();
this._combinationOperatorModel.reset();
}

// eslint-disable-next-line valid-jsdoc
/**
* Reset the model to its default state
*
* @return {void}
* @inheritDoc
*/
reset() {
this._selectionModel.reset();
this._combinationOperatorModel.reset();
get isEmpty() {
return this._selectionModel.isEmpty;
}

// eslint-disable-next-line valid-jsdoc
/**
* @inheritDoc
*/
get normalized() {
return {
values: this.selected.join(),
operation: this.combinationOperator,
};
}

/**
Expand Down Expand Up @@ -87,13 +96,4 @@ export class TagFilterModel extends Observable {
get combinationOperator() {
return this._combinationOperatorModel.current;
}

/**
* Returns an observable notified any time a visual change occurs that has no impact on the actual selection
*
* @return {Observable} the visual change observable
*/
get visualChange$() {
return this._selectionModel.visualChange$;
}
}

This file was deleted.

9 changes: 9 additions & 0 deletions lib/public/components/common/selection/SelectionModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,15 @@ export class SelectionModel extends Observable {
return [...new Set(this._selectedOptions.map(({ value }) => value))];
}

/**
* States if the current selection is empty
*
* @return {boolean} true if the selection is empty
*/
get isEmpty() {
return this.selected.length === 0;
}

/**
* If the selection allows one and only one selection, current will return the currently selected option. In any other case it will throw an
* error
Expand Down
14 changes: 11 additions & 3 deletions lib/public/utilities/fetch/buildUrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ export const buildUrl = (baseURL, parameters) => {
return url;
}

/**
* Sanitize a value to be used as URL parameter key or value
*
* @param {string} value the value to sanitize
* @return {string} the sanitized value
*/
const sanitize = (value) => encodeURIComponent(decodeURIComponent(value));

/**
* Stringify a query parameter to be used in a URL and push it in the serialized query parameters list
*
Expand All @@ -111,16 +119,16 @@ export const buildUrl = (baseURL, parameters) => {

if (typeof value === 'object' && value !== null) {
for (const [subKey, subValue] of Object.entries(value)) {
formatAndPushQueryParameter(`${key}[${subKey}]`, subValue);
formatAndPushQueryParameter(`${key}[${sanitize(subKey)}]`, subValue);
}
return;
}

serializedQueryParameters.push(`${key}=${value}`);
serializedQueryParameters.push(`${key}=${sanitize(value)}`);
};

for (const [key, parameter] of Object.entries(parameters)) {
formatAndPushQueryParameter(key, parameter);
formatAndPushQueryParameter(sanitize(key), parameter);
}

return `${url}?${serializedQueryParameters.join('&')}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { PaginationModel } from '../../../components/Pagination/PaginationModel.js';
import { Observable, RemoteData } from '/js/src/index.js';
import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js';
import { buildUrl } from '../../../utilities/fetch/buildUrl.js';

/**
* Environment overview page model
Expand Down Expand Up @@ -52,7 +53,7 @@ export class EnvironmentOverviewModel extends Observable {
'page[limit]': this._pagination.itemsPerPage,
};

const endpoint = `/api/environments?${new URLSearchParams(params).toString()}`;
const endpoint = buildUrl('/api/environments', params);

try {
const { items, totalCount } = await getRemoteDataSlice(endpoint);
Expand Down
3 changes: 2 additions & 1 deletion lib/public/views/Flps/Flps.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import { fetchClient, Observable, RemoteData } from '/js/src/index.js';
import { PaginationModel } from '../../components/Pagination/PaginationModel.js';
import { buildUrl } from '../../utilities/fetch/buildUrl.js';

/**
* Model representing handlers for homePage.js
Expand Down Expand Up @@ -53,7 +54,7 @@ export default class Overview extends Observable {
'page[limit]': this._pagination.itemsPerPage,
};

const endpoint = `/api/flps?${new URLSearchParams(params).toString()}`;
const endpoint = buildUrl('/api/flps', params);
const response = await fetchClient(endpoint, { method: 'GET' });
const result = await response.json();

Expand Down
3 changes: 2 additions & 1 deletion lib/public/views/Home/Overview/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const MIN_ROWS = 5;

/**
* Home Page component
* @param {Object} model global model
*
* @param {Model} model global model
* @return {Component} Return the component of the home page
*/
export const HomePage = ({ home: { logsOverviewModel, runsOverviewModel, lhcFillsOverviewModel } }) => {
Expand Down
6 changes: 6 additions & 0 deletions lib/public/views/Logs/ActiveColumns/logsActiveColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ export const logsActiveColumns = {
sortable: true,
size: 'w-15',
format: (tags) => formatTagsList(tags),

/**
* Tag filter component
* @param {LogsOverviewModel} logsModel the log model
* @return {Component} the filter component
*/
filter: (logsModel) => tagFilter(logsModel.listingTagsFilterModel),
balloon: true,
profiles: [profiles.none, 'embeded'],
Expand Down
7 changes: 4 additions & 3 deletions lib/public/views/Logs/Overview/LogsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { FilterInputModel } from '../../../components/Filters/common/filters/Fil
import { AuthorFilterModel } from '../../../components/Filters/LogsFilter/author/AuthorFilterModel.js';
import { PaginationModel } from '../../../components/Pagination/PaginationModel.js';
import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js';
import { buildUrl } from '../../../utilities/fetch/buildUrl.js';

/**
* Model representing handlers for log entries page
Expand Down Expand Up @@ -90,7 +91,7 @@ export class LogsOverviewModel extends Observable {
'page[limit]': this._pagination.itemsPerPage,
};

const endpoint = `/api/logs?${new URLSearchParams(params).toString()}`;
const endpoint = buildUrl('/api/logs', params);

try {
const { items, totalCount } = await getRemoteDataSlice(endpoint);
Expand Down Expand Up @@ -158,7 +159,7 @@ export class LogsOverviewModel extends Observable {
|| !this._authorFilter.isEmpty
|| this.createdFilterFrom !== ''
|| this.createdFilterTo !== ''
|| !this.listingTagsFilterModel.isEmpty()
|| !this.listingTagsFilterModel.isEmpty
|| this.runFilterValues.length !== 0
|| this.environmentFilterValues.length !== 0
|| this.lhcFillFilterValues.length !== 0
Expand Down Expand Up @@ -395,7 +396,7 @@ export class LogsOverviewModel extends Observable {
'filter[created][to]':
new Date(`${this.createdFilterTo.replace(/\//g, '-')}T23:59:59.999`).getTime(),
},
...!this.listingTagsFilterModel.isEmpty() && {
...!this.listingTagsFilterModel.isEmpty && {
'filter[tags][values]': this.listingTagsFilterModel.selected.join(),
'filter[tags][operation]': this.listingTagsFilterModel.combinationOperator,
},
Expand Down
Loading

0 comments on commit 3fc88c9

Please sign in to comment.