From f472da175387fdcc56b7076021e00d5918a2cf13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Fri, 2 Jun 2017 15:23:47 +0200 Subject: [PATCH] Refactor URLParamsHelper into TS service --- .../wp-query/url-params-helper.test.ts} | 46 ++-- .../components/wp-query/url-params-helper.ts | 240 ++++++++++++++++++ frontend/app/helpers/index.js | 2 - frontend/app/helpers/url-params-helper.ts | 232 ----------------- 4 files changed, 263 insertions(+), 257 deletions(-) rename frontend/{tests/unit/tests/helpers/url-params-helper-test.js => app/components/wp-query/url-params-helper.test.ts} (90%) create mode 100644 frontend/app/components/wp-query/url-params-helper.ts delete mode 100644 frontend/app/helpers/url-params-helper.ts diff --git a/frontend/tests/unit/tests/helpers/url-params-helper-test.js b/frontend/app/components/wp-query/url-params-helper.test.ts similarity index 90% rename from frontend/tests/unit/tests/helpers/url-params-helper-test.js rename to frontend/app/components/wp-query/url-params-helper.test.ts index d9c62f6b3b..d8200eac65 100644 --- a/frontend/tests/unit/tests/helpers/url-params-helper-test.js +++ b/frontend/app/components/wp-query/url-params-helper.test.ts @@ -29,20 +29,20 @@ /*jshint expr: true*/ describe('UrlParamsHelper', function() { - var UrlParamsHelper, PathHelper; + let UrlParamsHelper:any, PathHelper:any; beforeEach(angular.mock.module('openproject.helpers', 'openproject.models')); - beforeEach(inject(function(_UrlParamsHelper_, _PathHelper_) { + beforeEach(inject(function(_UrlParamsHelper_:any, _PathHelper_:any) { UrlParamsHelper = _UrlParamsHelper_; PathHelper = _PathHelper_; })); describe('buildQueryString', function() { - var params = { + const params = { ids: [1, 2, 3], str: '@#$%' }; - var queryString; + let queryString:string; beforeEach(function() { queryString = UrlParamsHelper.buildQueryString(params); @@ -58,11 +58,11 @@ describe('UrlParamsHelper', function() { }); describe('encodeQueryJsonParams', function(){ - var query; - var additional; + let query:any; + let additional:any; beforeEach(function() { - var filter1 = { + let filter1 = { id: 'soße', name: 'soße_id', type: 'list_model', @@ -74,7 +74,7 @@ describe('UrlParamsHelper', function() { }, values: ['knoblauch'] }; - var filter2 = { + let filter2 = { id: 'created_at', type: 'datetime_past', operator: { @@ -109,23 +109,23 @@ describe('UrlParamsHelper', function() { }); it('should encode query to params JSON', function() { - var encodedJSON = UrlParamsHelper.encodeQueryJsonParams(query, additional); - var expectedJSON = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}"; + let encodedJSON = UrlParamsHelper.encodeQueryJsonParams(query, additional); + let expectedJSON = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}"; expect(encodedJSON).to.eq(expectedJSON); }); }); describe('buildV3GetQueryFromJsonParams', function() { - var params; + let params:string; beforeEach(function() { params = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc,status:asc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}"; }); it('should decode query params to object', function() { - var decodedQueryParams = UrlParamsHelper.buildV3GetQueryFromJsonParams(params); + let decodedQueryParams = UrlParamsHelper.buildV3GetQueryFromJsonParams(params); - var expected = { + let expected = { 'columns[]': ['type', 'status', 'soße'], showSums: true, timelineVisible: true, @@ -156,11 +156,11 @@ describe('UrlParamsHelper', function() { }); describe('buildV3GetQueryFromQueryResource', function() { - var query; - var additional; + let query:any; + let additional:any; it('decodes query params to object', function() { - var filter1 = { + let filter1 = { id: 'soße', name: 'soße_id', type: 'list_model', @@ -172,7 +172,7 @@ describe('UrlParamsHelper', function() { }, values: ['knoblauch'] }; - var filter2 = { + let filter2 = { id: 'created_at', type: 'datetime_past', operator: { @@ -205,11 +205,11 @@ describe('UrlParamsHelper', function() { additional = { offset: 10, pageSize: 100 - } + }; - var v3Params = UrlParamsHelper.buildV3GetQueryFromQueryResource(query, additional); + let v3Params = UrlParamsHelper.buildV3GetQueryFromQueryResource(query, additional); - var expected = { + let expected = { 'columns[]': ['type', 'status', 'soße'], showSums: true, groupBy: 'status', @@ -236,7 +236,7 @@ describe('UrlParamsHelper', function() { }); it('decodes string object filters', function() { - var filter1 = { + let filter1 = { id: 'customField1', operator: { id: '=' @@ -266,9 +266,9 @@ describe('UrlParamsHelper', function() { additional = {} - var v3Params = UrlParamsHelper.buildV3GetQueryFromQueryResource(query, additional); + let v3Params = UrlParamsHelper.buildV3GetQueryFromQueryResource(query, additional); - var expected = { + let expected = { 'columns[]': [], filters: JSON.stringify([ { diff --git a/frontend/app/components/wp-query/url-params-helper.ts b/frontend/app/components/wp-query/url-params-helper.ts new file mode 100644 index 0000000000..24461af3f8 --- /dev/null +++ b/frontend/app/components/wp-query/url-params-helper.ts @@ -0,0 +1,240 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2017 the OpenProject Foundation (OPF) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2017 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See doc/COPYRIGHT.rdoc for more details. +//++ + +import {QuerySortByResource} from "../api/api-v3/hal-resources/query-sort-by-resource.service"; +import {QueryResource} from "../api/api-v3/hal-resources/query-resource.service"; + +export class UrlParamsHelperService { + + public constructor(public PaginationService:any) { + + } + + // copied more or less from angular buildUrl + public buildQueryString(params:any) { + if (!params) return; + + var parts:string[] = []; + angular.forEach(params, function (value, key) { + if (!value) return; + if (!Array.isArray(value)) value = [value]; + + angular.forEach(value, function (v) { + if (v !== null && typeof v === 'object') { + v = JSON.stringify(v); + } + parts.push(encodeURIComponent(key) + '=' + + encodeURIComponent(v)); + }); + }); + + return parts.join('&'); + } + + public encodeQueryJsonParams(query:QueryResource, additional:any) { + var paramsData:any = { + c: query.columns.map(function (column) { + return column.id; + }) + }; + if (!!query.sums) { + paramsData.s = query.sums; + } + + if (!!query.timelineVisible) { + paramsData.tv = query.timelineVisible; + } + + paramsData.tzl = query.timelineZoomLevel; + paramsData.hi = !!query.showHierarchies; + + if (query.groupBy) { + paramsData.g = query.groupBy.id; + } + if (query.sortBy) { + paramsData.t = query + .sortBy + .map(function (sort:QuerySortByResource) { + return sort.id.replace('-', ':') + }) + .join(); + } + if (query.filters && query.filters.length) { + paramsData.f = query + .filters + .map((filter:any) => { + var id = filter.id; + + var operator = filter.operator.id; + + return { + n: id, + o: encodeURIComponent(operator), + v: _.map(filter.values, (v) => this.queryFilterValueToParam(v)) + }; + }); + } else { + paramsData.f = []; + } + + paramsData.pa = additional.page; + paramsData.pp = additional.perPage; + + return JSON.stringify(paramsData); + } + + public buildV3GetQueryFromJsonParams(updateJson:any) { + var queryData:any = { + pageSize: this.PaginationService.getPerPage() + } + + if (!updateJson) { + return queryData; + } + + var properties = JSON.parse(updateJson); + + if (properties.c) { + queryData["columns[]"] = properties.c.map((column:any) => column); + } + if (!!properties.s) { + queryData.showSums = properties.s; + } + if (!!properties.tv) { + queryData.timelineVisible = properties.tv; + } + + if (properties.tzl) { + queryData.timelineZoomLevel = properties.tzl; + } + + if (properties.hi === false || properties.hi === true) { + queryData.showHierarchies = properties.hi; + } + + if (properties.g) { + queryData.groupBy = properties.g; + } + + // Filters + if (properties.f) { + var filters = properties.f.map(function (urlFilter:any) { + var attributes = { + operator: decodeURIComponent(urlFilter.o) + } + if (urlFilter.v) { + // the array check is only there for backwards compatibility reasons. + // Nowadays, it will always be an array; + var vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v]; + angular.extend(attributes, {values: vs}); + } + const filterData:any = {}; + filterData[urlFilter.n] = attributes; + + return filterData; + }); + + queryData.filters = JSON.stringify(filters); + } + + // Sortation + if (properties.t) { + queryData.sortBy = JSON.stringify(properties.t.split(',').map((sort:any) => sort.split(':'))); + } + + // Pagination + if (properties.pa) { + queryData.offset = properties.pa; + } + if (properties.pp) { + queryData.pageSize = properties.pp; + } + + return queryData; + } + + public buildV3GetQueryFromQueryResource(query:QueryResource, additionalParams:any) { + var queryData:any = {}; + + queryData["columns[]"] = query.columns.map((column:any) => column.id); + + queryData.showSums = query.sums; + queryData.timelineVisible = query.timelineVisible; + queryData.timelineZoomLevel = query.timelineZoomLevel; + queryData.showHierarchies = query.showHierarchies; + + if (query.groupBy) { + queryData.groupBy = query.groupBy.id; + } + + // Filters + const filters = query.filters.map((filter:any) => { + let id = filter.filter.$href; + id = id.substring(id.lastIndexOf('/') + 1, id.length); + + const operator = filter.operator.id; + const values = _.map(filter.values, (v) => this.queryFilterValueToParam(v)); + const filterHash:any = {}; + filterHash[id] = {operator: operator, values: values}; + + return filterHash; + }); + + queryData.filters = JSON.stringify(filters); + + // Sortation + queryData.sortBy = JSON.stringify(query + .sortBy + .map(function (sort:QuerySortByResource) { + return sort.id.split('-') + })); + + return angular.extend(queryData, additionalParams); + } + + public queryFilterValueToParam(value:any) { + if (typeof(value) === 'boolean') { + return value ? 't' : 'f'; + } + + if (!value) { + return ''; + } else if (value.id) { + return value.id.toString(); + } else if (value.$href && value.$href.match(/^\/api\/v3\/string_objects/i)) { + return value.$href.match(/value=([^&]+)/)[1].toString(); + } else if (value.$href) { + return value.$href.split('/').pop().toString(); + } else { + return value.toString(); + } + } +} + +angular.module('openproject.helpers') + .service('UrlParamsHelper', UrlParamsHelperService); diff --git a/frontend/app/helpers/index.js b/frontend/app/helpers/index.js index 2ea30dffc3..bd0c439753 100644 --- a/frontend/app/helpers/index.js +++ b/frontend/app/helpers/index.js @@ -32,7 +32,5 @@ angular.module('openproject.helpers') .service('CustomFieldHelper', ['CUSTOM_FIELD_PREFIX', 'I18n', require( './custom-field-helper')]) .factory('SvgHelper', require('./svg-helper')) - .service('UrlParamsHelper', ['PaginationService', - require('./url-params-helper')]) .service('WorkPackageLoadingHelper', ['$timeout', require( './work-package-loading-helper')]); diff --git a/frontend/app/helpers/url-params-helper.ts b/frontend/app/helpers/url-params-helper.ts deleted file mode 100644 index 47fbdb36df..0000000000 --- a/frontend/app/helpers/url-params-helper.ts +++ /dev/null @@ -1,232 +0,0 @@ -//-- copyright -// OpenProject is a project management system. -// Copyright (C) 2012-2017 the OpenProject Foundation (OPF) -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License version 3. -// -// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -// Copyright (C) 2006-2017 Jean-Philippe Lang -// Copyright (C) 2010-2013 the ChiliProject Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// See doc/COPYRIGHT.rdoc for more details. -//++ - -import {QueryColumn} from "../components/wp-query/query-column"; -import {QueryFilterResource} from "../components/api/api-v3/hal-resources/query-filter-resource.service"; -import {QuerySortByResource} from "../components/api/api-v3/hal-resources/query-sort-by-resource.service"; -import {QueryResource} from "../components/api/api-v3/hal-resources/query-resource.service"; -module.exports = function(PaginationService:any) { - var UrlParamsHelper = { - // copied more or less from angular buildUrl - buildQueryString: function(params:any) { - if (!params) return; - - var parts:string[] = []; - angular.forEach(params, function(value, key) { - if (!value) return; - if (!Array.isArray(value)) value = [value]; - - angular.forEach(value, function(v) { - if (v !== null && typeof v === 'object') { - v = JSON.stringify(v); - } - parts.push(encodeURIComponent(key) + '=' + - encodeURIComponent(v)); - }); - }); - - return parts.join('&'); - }, - - encodeQueryJsonParams: function(query:QueryResource, additional:any) { - var paramsData:any = { - c: query.columns.map(function(column) { return column.id; }) - }; - if(!!query.sums) { - paramsData.s = query.sums; - } - - if(!!query.timelineVisible) { - paramsData.tv = query.timelineVisible; - } - - paramsData.tzl = query.timelineZoomLevel; - paramsData.hi = !!query.showHierarchies; - - if(query.groupBy) { - paramsData.g = query.groupBy.id; - } - if(query.sortBy) { - paramsData.t = query - .sortBy - .map(function(sort:QuerySortByResource) { return sort.id.replace('-', ':') }) - .join(); - } - if(query.filters && query.filters.length) { - paramsData.f = query - .filters - .map((filter:any) => { - var id = filter.id; - - var operator = filter.operator.id; - - return { - n: id, - o: encodeURIComponent(operator), - v: _.map(filter.values, UrlParamsHelper.queryFilterValueToParam) - }; - }); - } else { - paramsData.f = []; - } - - paramsData.pa = additional.page; - paramsData.pp = additional.perPage; - - return JSON.stringify(paramsData); - }, - - buildV3GetQueryFromJsonParams: function(updateJson:any) { - var queryData:any = { - pageSize: PaginationService.getPerPage() - } - - if (!updateJson) { - return queryData; - } - - var properties = JSON.parse(updateJson); - - if(properties.c) { - queryData["columns[]"] = properties.c.map((column:any) => column); - } - if(!!properties.s) { - queryData.showSums = properties.s; - } - if(!!properties.tv) { - queryData.timelineVisible = properties.tv; - } - - if (properties.tzl) { - queryData.timelineZoomLevel = properties.tzl; - } - - if(properties.hi === false || properties.hi === true) { - queryData.showHierarchies = properties.hi; - } - - if(properties.g) { - queryData.groupBy = properties.g; - } - - // Filters - if(properties.f) { - var filters = properties.f.map(function(urlFilter:any) { - var attributes = { - operator: decodeURIComponent(urlFilter.o) - } - if(urlFilter.v) { - // the array check is only there for backwards compatibility reasons. - // Nowadays, it will always be an array; - var vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v]; - angular.extend(attributes, { values: vs }); - } - const filterData:any = {}; - filterData[urlFilter.n] = attributes; - - return filterData; - }); - - queryData.filters = JSON.stringify(filters); - } - - // Sortation - if(properties.t) { - queryData.sortBy = JSON.stringify(properties.t.split(',').map((sort:any) => sort.split(':'))); - } - - // Pagination - if(properties.pa) { - queryData.offset = properties.pa; - } - if(properties.pp) { - queryData.pageSize = properties.pp; - } - - return queryData; - }, - - buildV3GetQueryFromQueryResource: function(query:QueryResource, additionalParams:any) { - var queryData:any = {}; - - queryData["columns[]"] = query.columns.map((column:any) => column.id); - - queryData.showSums = query.sums; - queryData.timelineVisible = query.timelineVisible; - queryData.timelineZoomLevel = query.timelineZoomLevel; - queryData.showHierarchies = query.showHierarchies; - - if(query.groupBy) { - queryData.groupBy = query.groupBy.id; - } - - // Filters - const filters = query.filters.map((filter:any) => { - let id = filter.filter.$href; - id = id.substring(id.lastIndexOf('/') + 1, id.length); - - const operator = filter.operator.id; - const values = _.map(filter.values, UrlParamsHelper.queryFilterValueToParam); - const filterHash:any = {}; - filterHash[id] = { operator: operator, values: values }; - - return filterHash; - }); - - queryData.filters = JSON.stringify(filters); - - // Sortation - queryData.sortBy = JSON.stringify(query - .sortBy - .map(function(sort:QuerySortByResource) { return sort.id.split('-') })); - - - return angular.extend(queryData, additionalParams); - }, - - queryFilterValueToParam: function(value:any) { - if (typeof(value) === 'boolean') { - return value ? 't': 'f'; - } - - if (!value) { - return ''; - } else if (value.id) { - return value.id.toString(); - } else if (value.$href && value.$href.match(/^\/api\/v3\/string_objects/i)) { - return value.$href.match(/value=([^&]+)/)[1].toString(); - } else if (value.$href) { - return value.$href.split('/').pop().toString(); - } else { - return value.toString(); - } - } - }; - - return UrlParamsHelper; -};