From e0a076928c737f6e732c90633fb4596561222960 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 6 Feb 2020 13:33:39 +0300 Subject: [PATCH 01/18] replace querystring (querystring-browser) -> query-string --- .eslintrc.js | 8 +------ package.json | 1 + .../lib/get_webpack_config.js | 1 - src/core/server/http/http_server.mocks.ts | 6 +++--- src/core/utils/url.ts | 4 ++-- src/legacy/server/logging/log_format.js | 2 +- .../ui/ui_exports/ui_export_defaults.js | 1 - .../editor/legacy/console_editor/editor.tsx | 10 ++++----- src/plugins/console/public/lib/es/es.ts | 2 +- .../query/timefilter/lib/parse_querystring.ts | 4 ++-- .../public/history/remove_query_param.ts | 2 +- .../public/state_management/url/format.ts | 4 ++-- .../url/stringify_query_string.ts | 21 ++++++++++++------- .../server/series_functions/quandl.test.js | 2 +- .../components/shared/Links/url_helpers.ts | 18 ++++++++++------ .../public/containers/with_url_state.tsx | 2 +- .../plugins/canvas/public/lib/app_state.ts | 2 +- .../plugins/canvas/public/lib/modify_url.ts | 4 ++-- .../public/app/services/query_params.js | 2 +- .../public/app/services/routing.js | 2 +- .../analytics_job_exploration.tsx | 4 ++-- .../routes/datavisualizer/index_based.tsx | 4 +--- .../routing/routes/new_job/job_type.tsx | 3 +-- .../routing/routes/new_job/recognize.tsx | 8 ++++--- .../routing/routes/new_job/wizard.tsx | 3 +-- .../ml/public/application/util/url_state.ts | 5 ++--- .../public/app/services/query_params.js | 2 +- .../reporting/public/lib/reporting_client.ts | 2 +- .../server/routes/api/index_patterns.js | 2 +- .../home/snapshot_list/snapshot_list.tsx | 2 +- .../repository_add/repository_add.tsx | 2 +- .../uptime/public/hooks/use_url_params.ts | 10 ++++++--- .../public/lib/helper/stringify_url_params.ts | 2 +- .../plugins/uptime/public/state/api/ping.ts | 4 ++-- x-pack/package.json | 3 ++- x-pack/plugins/spaces/server/lib/utils/url.ts | 4 ++-- .../rollup/index_patterns_extensions.js | 2 +- .../apis/security/saml_login.ts | 2 +- .../fixtures/saml_tools.ts | 2 +- yarn.lock | 19 +++++++++++++++++ 40 files changed, 104 insertions(+), 79 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 199f3743fd621..ffa8e3ec3840c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -353,13 +353,7 @@ module.exports = { settings: { // instructs import/no-extraneous-dependencies to treat certain modules // as core modules, even if they aren't listed in package.json - 'import/core-modules': [ - 'plugins', - 'legacy/ui', - 'uiExports', - // TODO: Remove once https://github.com/benmosher/eslint-plugin-import/issues/1374 is fixed - 'querystring', - ], + 'import/core-modules': ['plugins', 'legacy/ui', 'uiExports'], 'import/resolver': { '@kbn/eslint-import-resolver-kibana': { diff --git a/package.json b/package.json index a79d41a67f580..866adfa4f6caf 100644 --- a/package.json +++ b/package.json @@ -231,6 +231,7 @@ "proxy-from-env": "1.0.0", "pug": "^2.0.4", "querystring-browser": "1.0.4", + "query-string": "6.10.1", "raw-loader": "3.1.0", "react": "^16.12.0", "react-color": "^2.13.8", diff --git a/packages/kbn-eslint-import-resolver-kibana/lib/get_webpack_config.js b/packages/kbn-eslint-import-resolver-kibana/lib/get_webpack_config.js index e02c38494991a..da0b799b338ed 100755 --- a/packages/kbn-eslint-import-resolver-kibana/lib/get_webpack_config.js +++ b/packages/kbn-eslint-import-resolver-kibana/lib/get_webpack_config.js @@ -29,7 +29,6 @@ exports.getWebpackConfig = function(kibanaPath, projectRoot, config) { // Kibana defaults https://github.com/elastic/kibana/blob/6998f074542e8c7b32955db159d15661aca253d7/src/legacy/ui/ui_bundler_env.js#L30-L36 ui: fromKibana('src/legacy/ui/public'), test_harness: fromKibana('src/test_harness/public'), - querystring: 'querystring-browser', // Dev defaults for test bundle https://github.com/elastic/kibana/blob/6998f074542e8c7b32955db159d15661aca253d7/src/core_plugins/tests_bundle/index.js#L73-L78 ng_mock$: fromKibana('src/test_utils/public/ng_mock'), diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index 230a229b36888..cf54241c2f228 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -19,8 +19,7 @@ import { Request } from 'hapi'; import { merge } from 'lodash'; import { Socket } from 'net'; - -import querystring from 'querystring'; +import { stringify } from 'query-string'; import { schema } from '@kbn/config-schema'; @@ -55,7 +54,8 @@ function createKibanaRequestMock({ socket = new Socket(), routeTags, }: RequestFixtureOptions = {}) { - const queryString = querystring.stringify(query); + const queryString = stringify(query); + return KibanaRequest.from( createRawRequestMock({ headers, diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index 67b379e729ca4..869adbc600b84 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ParsedUrlQuery } from 'querystring'; +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -33,7 +33,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: ParsedUrlQuery; + query: ParsedQuery; } /** diff --git a/src/legacy/server/logging/log_format.js b/src/legacy/server/logging/log_format.js index 0e284df230ef4..2f3c7010a0080 100644 --- a/src/legacy/server/logging/log_format.js +++ b/src/legacy/server/logging/log_format.js @@ -23,7 +23,7 @@ import { get, _ } from 'lodash'; import numeral from '@elastic/numeral'; import chalk from 'chalk'; import stringify from 'json-stringify-safe'; -import querystring from 'querystring'; +import querystring from 'query-string'; import applyFiltersToKeys from './apply_filters_to_keys'; import { inspect } from 'util'; import { logWithMetadata } from './log_with_metadata'; diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js index 1cb23d2ad2a23..459559e84b1a7 100644 --- a/src/legacy/ui/ui_exports/ui_export_defaults.js +++ b/src/legacy/ui/ui_exports/ui_export_defaults.js @@ -30,7 +30,6 @@ export const UI_EXPORT_DEFAULTS = { ui: resolve(ROOT, 'src/legacy/ui/public'), __kibanaCore__$: resolve(ROOT, 'src/core/public'), test_harness: resolve(ROOT, 'src/test_harness/public'), - querystring: 'querystring-browser', moment$: resolve(ROOT, 'webpackShims/moment'), 'moment-timezone$': resolve(ROOT, 'webpackShims/moment-timezone'), }, diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index b3e966ddffa4c..069a766a14bc2 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -21,10 +21,7 @@ import React, { CSSProperties, useCallback, useEffect, useRef, useState } from ' import { EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { debounce } from 'lodash'; - -// Node v5 querystring for browser. -// @ts-ignore -import * as qs from 'querystring-browser'; +import { parse } from 'query-string'; import { EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useServicesContext, useEditorReadContext } from '../../../../contexts'; @@ -96,9 +93,9 @@ function EditorUI({ initialTextValue }: EditorProps) { editorInstanceRef.current = senseEditor.create(editorRef.current!); const editor = editorInstanceRef.current; - const readQueryParams = () => { + const readQueryParams = (): Record => { const [, queryString] = (window.location.hash || '').split('?'); - return qs.parse(queryString || ''); + return parse(queryString || ''); }; const loadBufferFromRemote = (url: string) => { @@ -138,6 +135,7 @@ function EditorUI({ initialTextValue }: EditorProps) { window.addEventListener('hashchange', onHashChange); const initialQueryParams = readQueryParams(); + if (initialQueryParams.load_from) { loadBufferFromRemote(initialQueryParams.load_from); } else { diff --git a/src/plugins/console/public/lib/es/es.ts b/src/plugins/console/public/lib/es/es.ts index 52aba98d9e662..7197395368be9 100644 --- a/src/plugins/console/public/lib/es/es.ts +++ b/src/plugins/console/public/lib/es/es.ts @@ -17,7 +17,7 @@ * under the License. */ -import { stringify as formatQueryString } from 'querystring'; +import { stringify as formatQueryString } from 'query-string'; import $ from 'jquery'; const esVersion: string[] = []; diff --git a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts index 467110b6f32ea..6ce4faf5c7488 100644 --- a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts +++ b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts @@ -17,7 +17,7 @@ * under the License. */ -import qs from 'querystring'; +import { parse } from 'query-string'; export function parseQueryString() { // window.location.search is an empty string @@ -27,5 +27,5 @@ export function parseQueryString() { return {}; } - return qs.parse(hrefSplit[1]); + return parse(hrefSplit[1]); } diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index fbf985998b4cd..5531cab07ae8c 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -18,7 +18,7 @@ */ import { History, Location } from 'history'; -import { parse } from 'querystring'; +import { parse } from 'query-string'; import { stringifyQueryString } from '../state_management/url/stringify_query_string'; // TODO: extract it to ../url export function removeQueryParam(history: History, param: string, replace: boolean = true) { diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 988ee08627382..1247176db8b19 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -18,13 +18,13 @@ */ import { format as formatUrl } from 'url'; -import { ParsedUrlQuery } from 'querystring'; +import { ParsedQuery } from 'query-string'; import { parseUrl, parseUrlHash } from './parse'; import { stringifyQueryString } from './stringify_query_string'; export function replaceUrlHashQuery( rawUrl: string, - queryReplacer: (query: ParsedUrlQuery) => ParsedUrlQuery + queryReplacer: (query: ParsedQuery) => ParsedQuery ) { const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); diff --git a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts b/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts index e951dfac29c02..c91a22b4ff08d 100644 --- a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts +++ b/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts @@ -16,17 +16,24 @@ * specific language governing permissions and limitations * under the License. */ - -import { stringify, ParsedUrlQuery } from 'querystring'; +import { transform } from 'lodash'; +import { stringify } from 'query-string'; // encodeUriQuery implements the less-aggressive encoding done naturally by // the browser. We use it to generate the same urls the browser would -export const stringifyQueryString = (query: ParsedUrlQuery) => - stringify(query, undefined, undefined, { - // encode spaces with %20 is needed to produce the same queries as angular does - // https://github.com/angular/angular.js/blob/51c516e7d4f2d10b0aaa4487bd0b52772022207a/src/Angular.js#L1377 - encodeURIComponent: (val: string) => encodeUriQuery(val, true), +export const stringifyQueryString = (query: Record) => { + const encodedQuery = transform(query, (result, value, key) => { + if (key && value) { + result[key] = encodeUriQuery(value, true); + } + }); + + return stringify(encodedQuery, { + strict: false, + encode: false, + sort: false, }); +}; /** * Extracted from angular.js diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/timelion/server/series_functions/quandl.test.js index fe5aab512370f..e2e2ef31c36c4 100644 --- a/src/plugins/timelion/server/series_functions/quandl.test.js +++ b/src/plugins/timelion/server/series_functions/quandl.test.js @@ -20,7 +20,7 @@ import fn from './quandl'; const parseURL = require('url').parse; -const parseQueryString = require('querystring').parse; +const parseQueryString = require('query-string').parse; const tlConfig = require('./fixtures/tl_config')(); import moment from 'moment'; import fetchMock from 'node-fetch'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index 357ea23d522a0..c3fb49a64f1f8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -3,20 +3,26 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import qs from 'querystring'; +import { transform } from 'lodash'; +import { parse, stringify } from 'query-string'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; export function toQuery(search?: string): APMQueryParamsRaw { - return search ? qs.parse(search.slice(1)) : {}; + return search ? parse(search.slice(1)) : {}; } export function fromQuery(query: Record) { - return qs.stringify(query, undefined, undefined, { - encodeURIComponent: (value: string) => { - return encodeURIComponent(value).replace(/%3A/g, ':'); + const encodedQuery = transform(query, (result, value, key) => { + if (key && value) { + result[key] = encodeURIComponent(value).replace(/%3A/g, ':'); } }); + + return stringify(encodedQuery, { + strict: false, + encode: false, + sort: false + }); } export type APMQueryParams = { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx index 71e9163fe22e7..caff1eef91252 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx +++ b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse, stringify } from 'querystring'; +import { parse, stringify } from 'query-string'; import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { FlatObject } from '../frontend_types'; diff --git a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts index c93e505c595fd..a58b0f0c3bd17 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'querystring'; +import querystring from 'query-string'; import { get } from 'lodash'; // @ts-ignore untyped local import { getInitialState } from '../state/initial_state'; diff --git a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts index 890138c41d7bf..d128dc432e9cf 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ParsedUrlQuery } from 'querystring'; +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -20,7 +20,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: ParsedUrlQuery; + query: ParsedQuery; } /** diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js index f0871d62976ed..acf50350761ef 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'querystring'; +import { parse } from 'query-string'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js index bb4b4540b1922..57e43b2d79a1c 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js @@ -9,7 +9,7 @@ */ import { createLocation } from 'history'; -import { stringify } from 'querystring'; +import { stringify } from 'query-string'; import { APPS, BASE_PATH, BASE_PATH_REMOTE_CLUSTERS } from '../../../common/constants'; const isModifiedEvent = event => diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index 41c286c54836c..4374d424a1175 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -8,7 +8,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { decode } from 'rison-node'; -// @ts-ignore import queryString from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; @@ -36,7 +35,8 @@ export const analyticsJobExplorationRoute: MlRoute = { const PageWrapper: FC = ({ location, config, deps }) => { const { context } = useResolver('', undefined, config, basicResolvers(deps)); - const { _g } = queryString.parse(location.search); + const { _g }: Record = queryString.parse(location.search); + let globalState: any = null; try { globalState = decode(_g); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx index ab359238695d4..a8e7b07a19b1a 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx @@ -6,8 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; - -// @ts-ignore import queryString from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; @@ -37,7 +35,7 @@ export const indexBasedRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId } = queryString.parse(location.search); + const { index, savedSearchId }: Record = queryString.parse(location.search); const { context } = useResolver(index, savedSearchId, config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx index e537a186ec784..3c468a1adea34 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx @@ -7,7 +7,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -// @ts-ignore import queryString from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; @@ -33,7 +32,7 @@ export const jobTypeRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId } = queryString.parse(location.search); + const { index, savedSearchId }: Record = queryString.parse(location.search); const { context } = useResolver(index, savedSearchId, config, basicResolvers(deps)); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx index 4f5085facfb29..4448c28fd59df 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx @@ -6,7 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -// @ts-ignore import queryString from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -43,7 +42,7 @@ export const checkViewOrCreateRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { id, index, savedSearchId } = queryString.parse(location.search); + const { id, index, savedSearchId }: Record = queryString.parse(location.search); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), existingJobsAndGroups: mlJobService.getJobAndGroupIds, @@ -57,7 +56,10 @@ const PageWrapper: FC = ({ location, config, deps }) => { }; const CheckViewOrCreateWrapper: FC = ({ location, config, deps }) => { - const { id: moduleId, index: indexPatternId } = queryString.parse(location.search); + const { id: moduleId, index: indexPatternId }: Record = queryString.parse( + location.search + ); + // the single resolver checkViewOrCreateJobs redirects only. so will always reject useResolver(undefined, undefined, config, { checkViewOrCreateJobs: () => checkViewOrCreateJobs(moduleId, indexPatternId), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index 99c0511cd09ce..3c29fdcf0afcd 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -6,7 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -// @ts-ignore import queryString from 'query-string'; import { basicResolvers } from '../../resolvers'; @@ -123,7 +122,7 @@ export const categorizationRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, jobType, deps }) => { - const { index, savedSearchId } = queryString.parse(location.search); + const { index, savedSearchId }: Record = queryString.parse(location.search); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), privileges: checkCreateJobsPrivilege, diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index e7d5a94e2694f..5357fc1d85bb4 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -6,7 +6,6 @@ import { useCallback } from 'react'; import { isEqual } from 'lodash'; -// @ts-ignore import queryString from 'query-string'; import { decode, encode } from 'rison-node'; import { useHistory, useLocation } from 'react-router-dom'; @@ -33,12 +32,12 @@ function isRisonSerializationRequired(queryParam: string): boolean { export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; - const parsedQueryString = queryString.parse(search); + const parsedQueryString: Record = queryString.parse(search); try { Object.keys(parsedQueryString).forEach(a => { if (isRisonSerializationRequired(a)) { - urlState[a] = decode(parsedQueryString[a]) as Dictionary; + urlState[a] = decode(parsedQueryString[a]); } else { urlState[a] = parsedQueryString[a]; } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js index f0871d62976ed..acf50350761ef 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'querystring'; +import { parse } from 'query-string'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts index 9056c7967b4a8..16f2cad135a28 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts @@ -5,7 +5,7 @@ */ import { npStart } from 'ui/new_platform'; -import querystring from 'querystring'; +import querystring from 'query-string'; const { core } = npStart; diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js index dfc486c030812..a3ed59f10a4c4 100644 --- a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js +++ b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import Joi from 'joi'; +import querystring from 'query-string'; import { callWithRequestFactory } from '../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; @@ -11,7 +12,6 @@ import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory' import indexBy from 'lodash/collection/indexBy'; import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; import { mergeCapabilitiesWithFields } from '../../lib/merge_capabilities_with_fields'; -import querystring from 'querystring'; /** * Get list of fields for rollup index pattern, in the format of regular index pattern fields diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx index 642a12411e6f3..b47e79abead2f 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx @@ -6,7 +6,7 @@ import React, { Fragment, useState, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { parse } from 'querystring'; +import { parse } from 'query-string'; import { EuiButton, EuiCallOut, EuiLink, EuiEmptyPrompt, EuiSpacer, EuiIcon } from '@elastic/eui'; import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common/constants'; diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx index b4a76ff4329cf..f6f027af261e1 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx @@ -5,7 +5,7 @@ */ import React, { useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { parse } from 'querystring'; +import { parse } from 'query-string'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index e509e14223006..8e9174a36b129 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'querystring'; +import qs from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { UptimeUrlParams, getSupportedUrlParams } from '../lib/helper'; @@ -23,14 +23,18 @@ export const useUrlParams: UptimeUrlParamsHook = () => { search = location.search; } - const params = search ? { ...qs.parse(search[0] === '?' ? search.slice(1) : search) } : {}; + const params = search + ? { ...(qs.parse(search[0] === '?' ? search.slice(1) : search) as Record) } + : {}; return getSupportedUrlParams(params); }; const updateUrlParams: UpdateUrlParams = updatedParams => { if (!history || !location) return; const { pathname, search } = location; - const currentParams: any = qs.parse(search[0] === '?' ? search.slice(1) : search); + const currentParams: Record = qs.parse( + search[0] === '?' ? search.slice(1) : search + ); const mergedParams = { ...currentParams, ...updatedParams, diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts index 7d00a27d69032..d0055f56a2a70 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'querystring'; +import qs from 'query-string'; import { UptimeUrlParams } from './url_params'; import { CLIENT_DEFAULTS } from '../../../common/constants'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index e0c358fe40e71..cba76d6750384 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'querystring'; +import qs from 'query-string'; import { getApiPath } from '../../lib/helper'; import { APIFn } from './types'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; @@ -25,7 +25,7 @@ export const fetchPingHistogram: APIFn ...(statusFilter && { statusFilter }), ...(filters && { filters }), }; - const urlParams = qs.stringify(params).toString(); + const urlParams = qs.stringify(params); const response = await fetch(`${url}?${urlParams}`); if (!response.ok) { throw new Error(response.statusText); diff --git a/x-pack/package.json b/x-pack/package.json index 921f6ad991188..c1225f609ebbb 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -292,7 +292,9 @@ "proper-lockfile": "^3.2.0", "puid": "1.0.7", "puppeteer-core": "^1.19.0", + "query-string": "6.10.1", "raw-loader": "3.1.0", + "re-resizable": "^6.1.1", "react": "^16.12.0", "react-apollo": "^2.1.4", "react-beautiful-dnd": "^8.0.7", @@ -324,7 +326,6 @@ "request": "^2.88.0", "reselect": "3.0.1", "resize-observer-polyfill": "^1.5.0", - "re-resizable": "^6.1.1", "rison-node": "0.3.1", "rxjs": "^6.5.3", "semver": "5.7.0", diff --git a/x-pack/plugins/spaces/server/lib/utils/url.ts b/x-pack/plugins/spaces/server/lib/utils/url.ts index a5797c0f87868..c91934bb99f1f 100644 --- a/x-pack/plugins/spaces/server/lib/utils/url.ts +++ b/x-pack/plugins/spaces/server/lib/utils/url.ts @@ -8,7 +8,7 @@ // DIRECT COPY FROM `src/core/utils/url`, since it's not possible to import from there, // nor can I re-export from `src/core/server`... -import { ParsedUrlQuery } from 'querystring'; +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; export interface URLMeaningfulParts { @@ -19,7 +19,7 @@ export interface URLMeaningfulParts { protocol: string | null; slashes: boolean | null; port: string | null; - query: ParsedUrlQuery | {}; + query: ParsedQuery | {}; } /** diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index 8eb084f24c52f..f0bd0097491b9 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import querystring from 'querystring'; +import querystring from 'query-string'; import { registerHelpers } from './rollup.test_helpers'; import { INDEX_TO_ROLLUP_MAPPINGS, INDEX_PATTERNS_EXTENSION_BASE_PATH } from './constants'; diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 6ede8aadeb5a7..60d91962cd01c 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'querystring'; +import querystring from 'query-string'; import url from 'url'; import { delay } from 'bluebird'; import expect from '@kbn/expect'; diff --git a/x-pack/test/saml_api_integration/fixtures/saml_tools.ts b/x-pack/test/saml_api_integration/fixtures/saml_tools.ts index b7b94b8eeb17a..0e59eb53c25d7 100644 --- a/x-pack/test/saml_api_integration/fixtures/saml_tools.ts +++ b/x-pack/test/saml_api_integration/fixtures/saml_tools.ts @@ -6,7 +6,7 @@ import crypto from 'crypto'; import fs from 'fs'; -import querystring from 'querystring'; +import querystring from 'query-string'; import url from 'url'; import zlib from 'zlib'; import { promisify } from 'util'; diff --git a/yarn.lock b/yarn.lock index a35cd1d541762..f29215c2f8102 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23777,6 +23777,15 @@ qs@~6.4.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM= +query-string@6.10.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.10.1.tgz#30b3505f6fca741d5ae541964d1b3ae9dc2a0de8" + integrity sha512-SHTUV6gDlgMXg/AQUuLpTiBtW/etZ9JT6k6RCtCyqADquApLX0Aq5oK/s5UeTUAWBG50IExjIr587GqfXRfM4A== + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + query-string@^4.1.0, query-string@^4.2.2: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" @@ -27395,6 +27404,11 @@ spdy@^4.0.1: select-hose "^2.0.0" spdy-transport "^3.0.0" +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -27708,6 +27722,11 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + string-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" From 9e46ff9b6757d62f2e84e28728e18d617996bb91 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 6 Feb 2020 19:28:14 +0300 Subject: [PATCH 02/18] QueryString remove encode/decode methods --- src/core/server/http/http_server.mocks.ts | 2 +- src/legacy/server/logging/log_format.js | 2 +- .../public/state_management/global_state.js | 5 - src/legacy/ui/public/utils/query_string.d.ts | 2 - src/legacy/ui/public/utils/query_string.js | 101 ------------------ .../editor/legacy/console_editor/editor.tsx | 5 +- .../query/timefilter/lib/parse_querystring.ts | 2 +- .../public/history/remove_query_param.ts | 4 +- .../server/series_functions/quandl.test.js | 2 +- .../shared/Links/url_helpers.test.tsx | 2 +- .../components/shared/Links/url_helpers.ts | 2 +- .../public/containers/with_url_state.tsx | 15 ++- .../workpad_header/workpad_export/utils.ts | 6 +- x-pack/legacy/plugins/canvas/public/legacy.ts | 5 +- .../plugins/canvas/public/lib/app_state.ts | 4 +- .../legacy/plugins/canvas/public/plugin.tsx | 1 - .../public/app/services/query_params.js | 2 +- .../public/app/services/routing.js | 15 +-- .../analyze_in_ml_button.tsx | 6 +- .../plugins/infra/public/utils/url_state.tsx | 21 ++-- .../infra/public/utils/use_url_state.ts | 20 ++-- .../analytics_job_exploration.tsx | 4 +- .../routes/datavisualizer/index_based.tsx | 4 +- .../routing/routes/new_job/job_type.tsx | 4 +- .../routing/routes/new_job/recognize.tsx | 10 +- .../routing/routes/new_job/wizard.tsx | 4 +- .../routing/routes/timeseriesexplorer.tsx | 2 - .../ml/public/application/util/url_state.ts | 12 +-- .../public/app/services/query_params.js | 2 +- .../reporting/public/lib/reporting_client.ts | 4 +- .../server/routes/api/index_patterns.js | 4 +- .../ml_host_conditional_container.tsx | 26 +++-- .../ml_network_conditional_container.tsx | 25 +++-- .../public/components/url_state/helpers.ts | 30 ++++-- .../home/snapshot_list/snapshot_list.tsx | 2 +- .../repository_add/repository_add.tsx | 2 +- .../uptime/public/hooks/use_url_params.ts | 20 ++-- .../public/lib/helper/stringify_url_params.ts | 4 +- .../plugins/uptime/public/state/api/ping.ts | 4 +- .../rollup/index_patterns_extensions.js | 23 ++-- .../apis/security/saml_login.ts | 6 +- .../fixtures/saml_tools.ts | 4 +- 42 files changed, 174 insertions(+), 246 deletions(-) diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index cf54241c2f228..81d756f47d760 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -54,7 +54,7 @@ function createKibanaRequestMock({ socket = new Socket(), routeTags, }: RequestFixtureOptions = {}) { - const queryString = stringify(query); + const queryString = stringify(query, { sort: false }); return KibanaRequest.from( createRawRequestMock({ diff --git a/src/legacy/server/logging/log_format.js b/src/legacy/server/logging/log_format.js index 2f3c7010a0080..8a02dfe04646d 100644 --- a/src/legacy/server/logging/log_format.js +++ b/src/legacy/server/logging/log_format.js @@ -108,7 +108,7 @@ export default class TransformObjStream extends Stream.Transform { contentLength: contentLength, }; - const query = querystring.stringify(event.query); + const query = querystring.stringify(event.query, { sort: false }); if (query) data.req.url += '?' + query; data.message = data.req.method.toUpperCase() + ' '; diff --git a/src/legacy/ui/public/state_management/global_state.js b/src/legacy/ui/public/state_management/global_state.js index 955759e305950..d8ff38106b978 100644 --- a/src/legacy/ui/public/state_management/global_state.js +++ b/src/legacy/ui/public/state_management/global_state.js @@ -17,7 +17,6 @@ * under the License. */ -import { QueryString } from '../utils/query_string'; import { StateProvider } from './state'; import { uiModules } from '../modules'; import { createLegacyClass } from '../utils/legacy_class'; @@ -35,10 +34,6 @@ export function GlobalStateProvider(Private) { // if the url param is missing, write it back GlobalState.prototype._persistAcrossApps = true; - GlobalState.prototype.removeFromUrl = function(url) { - return QueryString.replaceParamInUrl(url, this._urlParam, null); - }; - return new GlobalState(); } diff --git a/src/legacy/ui/public/utils/query_string.d.ts b/src/legacy/ui/public/utils/query_string.d.ts index 959171443185e..3f3c1752d38b2 100644 --- a/src/legacy/ui/public/utils/query_string.d.ts +++ b/src/legacy/ui/public/utils/query_string.d.ts @@ -18,8 +18,6 @@ */ declare class QueryStringClass { - public decode(queryString: string): any; - public encode(obj: any): string; public param(key: string, value: string): string; } diff --git a/src/legacy/ui/public/utils/query_string.js b/src/legacy/ui/public/utils/query_string.js index 5fbc6da67bc98..6964cc7552425 100644 --- a/src/legacy/ui/public/utils/query_string.js +++ b/src/legacy/ui/public/utils/query_string.js @@ -21,109 +21,8 @@ import { encodeQueryComponent } from '../../../utils'; export const QueryString = {}; -/***** -/*** originally copied from angular, modified our purposes -/*****/ - -function tryDecodeURIComponent(value) { - try { - return decodeURIComponent(value); - } catch (e) { - // Ignore any invalid uri component - } // eslint-disable-line no-empty -} - -/** - * Parses an escaped url query string into key-value pairs. - * @returns {Object.} - */ -QueryString.decode = function(keyValue) { - const obj = {}; - let keyValueParts; - let key; - - (keyValue || '').split('&').forEach(function(keyValue) { - if (keyValue) { - keyValueParts = keyValue.split('='); - key = tryDecodeURIComponent(keyValueParts[0]); - if (key !== void 0) { - const val = keyValueParts[1] !== void 0 ? tryDecodeURIComponent(keyValueParts[1]) : true; - if (!obj[key]) { - obj[key] = val; - } else if (Array.isArray(obj[key])) { - obj[key].push(val); - } else { - obj[key] = [obj[key], val]; - } - } - } - }); - return obj; -}; - -/** - * Creates a queryString out of an object - * @param {Object} obj - * @return {String} - */ -QueryString.encode = function(obj) { - const parts = []; - const keys = Object.keys(obj).sort(); - keys.forEach(function(key) { - const value = obj[key]; - if (Array.isArray(value)) { - value.forEach(function(arrayValue) { - parts.push(QueryString.param(key, arrayValue)); - }); - } else { - parts.push(QueryString.param(key, value)); - } - }); - return parts.length ? parts.join('&') : ''; -}; - QueryString.param = function(key, val) { return ( encodeQueryComponent(key, true) + (val === true ? '' : '=' + encodeQueryComponent(val, true)) ); }; - -/** - * Extracts the query string from a url - * @param {String} url - * @return {Object} - returns an object describing the start/end index of the url in the string. The indices will be - * the same if the url does not have a query string - */ -QueryString.findInUrl = function(url) { - let qsStart = url.indexOf('?'); - let hashStart = url.lastIndexOf('#'); - - if (hashStart === -1) { - // out of bounds - hashStart = url.length; - } - - if (qsStart === -1) { - qsStart = hashStart; - } - - return { - start: qsStart, - end: hashStart, - }; -}; - -QueryString.replaceParamInUrl = function(url, param, newVal) { - const loc = QueryString.findInUrl(url); - const parsed = QueryString.decode(url.substring(loc.start + 1, loc.end)); - - if (newVal != null) { - parsed[param] = newVal; - } else { - delete parsed[param]; - } - - const chars = url.split(''); - chars.splice(loc.start, loc.end - loc.start, '?' + QueryString.encode(parsed)); - return chars.join(''); -}; diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 069a766a14bc2..c5d060845b50d 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -93,9 +93,10 @@ function EditorUI({ initialTextValue }: EditorProps) { editorInstanceRef.current = senseEditor.create(editorRef.current!); const editor = editorInstanceRef.current; - const readQueryParams = (): Record => { + const readQueryParams = () => { const [, queryString] = (window.location.hash || '').split('?'); - return parse(queryString || ''); + + return parse(queryString || '', { sort: false }) as Record; }; const loadBufferFromRemote = (url: string) => { diff --git a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts index 6ce4faf5c7488..e549ab04995f4 100644 --- a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts +++ b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts @@ -27,5 +27,5 @@ export function parseQueryString() { return {}; } - return parse(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index 5531cab07ae8c..c4161ef100a3b 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -24,8 +24,10 @@ import { stringifyQueryString } from '../state_management/url/stringify_query_st export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; const search = (oldLocation.search || '').replace(/^\?/, ''); - const query = parse(search); + const query = parse(search, { sort: false }); + delete query[param]; + const newSearch = stringifyQueryString(query); const newLocation: Location = { ...oldLocation, diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/timelion/server/series_functions/quandl.test.js index e2e2ef31c36c4..708d9c0d2588d 100644 --- a/src/plugins/timelion/server/series_functions/quandl.test.js +++ b/src/plugins/timelion/server/series_functions/quandl.test.js @@ -26,7 +26,7 @@ import moment from 'moment'; import fetchMock from 'node-fetch'; function parseUrlParams(url) { - return parseQueryString(parseURL(url).query); + return parseQueryString(parseURL(url).query, { sort: false }); } jest.mock('node-fetch', () => diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx index ac728e72fa877..f59f3fbab9f79 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx @@ -43,7 +43,7 @@ describe('fromQuery', () => { refreshPaused: true, refreshInterval: 5000 }) - ).toEqual('flyoutDetailTab=&refreshPaused=true&refreshInterval=5000'); + ).toEqual('refreshPaused=true&refreshInterval=5000'); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index c3fb49a64f1f8..6fd4960f4e583 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -8,7 +8,7 @@ import { parse, stringify } from 'query-string'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; export function toQuery(search?: string): APMQueryParamsRaw { - return search ? parse(search.slice(1)) : {}; + return search ? parse(search.slice(1), { sort: false }) : {}; } export function fromQuery(query: Record) { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx index caff1eef91252..c8f756da985a7 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx +++ b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx @@ -31,7 +31,9 @@ export class WithURLStateComponent extends React.Compon > { private get URLState(): URLState { // slice because parse does not account for the initial ? in the search string - return parse(decodeURIComponent(this.props.history.location.search).substring(1)) as URLState; + return parse(decodeURIComponent(this.props.history.location.search).substring(1), { + sort: false, + }) as URLState; } private historyListener: (() => void) | null = null; @@ -63,10 +65,13 @@ export class WithURLStateComponent extends React.Compon newState = state; } - const search: string = stringify({ - ...pastState, - ...newState, - }); + const search: string = stringify( + { + ...pastState, + ...newState, + }, + { sort: false } + ); const newLocation = { ...this.props.history.location, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index f7f191a48de82..b385ee74c740f 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +// todo: Should be removed +import { QueryString } from 'ui/utils/query_string'; + import rison from 'rison-node'; // @ts-ignore Untyped local. import { fetch } from '../../../../common/lib/fetch'; -import { getStartPlugins } from '../../../legacy'; import { CanvasWorkpad } from '../../../../types'; // type of the desired pdf output (print or preserve_layout) @@ -72,7 +74,7 @@ function getPdfUrlParts( export function getPdfUrl(...args: Arguments): string { const urlParts = getPdfUrlParts(...args); - return `${urlParts.createPdfUri}?${getStartPlugins().__LEGACY.QueryString.param( + return `${urlParts.createPdfUri}?${QueryString.param( 'jobParams', urlParts.createPdfPayload.jobParams )}`; diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index cbd2aa54627ee..cba683a12aeed 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -12,9 +12,7 @@ import chrome, { loadingCount } from 'ui/chrome'; // eslint-disable-line import/ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-disable-line import/order import { Storage } from '../../../../../src/plugins/kibana_utils/public'; // eslint-disable-line import/order // @ts-ignore Untyped Kibana Lib -import { formatMsg } from '../../../../../src/plugins/kibana_legacy/public'; // eslint-disable-line import/order -// @ts-ignore Untyped Kibana Lib -import { QueryString } from 'ui/utils/query_string'; // eslint-disable-line import/order +import { formatMsg } from 'ui/notify/lib/format_msg'; // eslint-disable-line import/order const shimCoreSetup = { ...npSetup.core, @@ -31,7 +29,6 @@ const shimStartPlugins: CanvasStartDeps = { absoluteToParsedUrl, // ToDo: Copy directly into canvas formatMsg, - QueryString, storage: Storage, // ToDo: Won't be a part of New Platform. Will need to handle internally trackSubUrlForApp: chrome.trackSubUrlForApp, diff --git a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts index a58b0f0c3bd17..d431202ba75a4 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'query-string'; +import { parse } from 'query-string'; import { get } from 'lodash'; // @ts-ignore untyped local import { getInitialState } from '../state/initial_state'; @@ -38,7 +38,7 @@ export function getDefaultAppState(): AppState { export function getCurrentAppState(): AppState { const history = historyProvider(getWindow()); const { search } = history.getLocation(); - const qs = !!search ? querystring.parse(search.replace(/^\?/, '')) : {}; + const qs = !!search ? parse(search.replace(/^\?/, ''), { sort: false }) : {}; const appState = assignAppState({}, qs); return appState; diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index 7928d46067908..5023ee11c544a 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -39,7 +39,6 @@ export interface CanvasStartDeps { __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; formatMsg: any; - QueryString: any; storage: typeof Storage; trackSubUrlForApp: Chrome['trackSubUrlForApp']; }; diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js index acf50350761ef..af462bfeffcf5 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return parse(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js index 57e43b2d79a1c..f156a49204a4a 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js @@ -22,16 +22,11 @@ const queryParamsFromObject = (params, encodeParams = false) => { return; } - const paramsStr = stringify( - params, - '&', - '=', - encodeParams - ? {} - : { - encodeURIComponent: val => val, // Don't encode special chars - } - ); + const paramsStr = stringify(params, { + encode: encodeParams, + sort: false, + }); + return `?${paramsStr}`; }; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index 536dd24faa7c1..19d7c691a5449 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -8,7 +8,7 @@ import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { encode } from 'rison-node'; -import { QueryString } from 'ui/utils/query_string'; +import { stringify } from 'query-string'; import url from 'url'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; @@ -61,7 +61,7 @@ const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRang }, }); - const hash = `/explorer?${QueryString.encode({ _g })}`; + const hash = `/explorer?${stringify({ _g })}`; return url.format({ pathname, @@ -94,7 +94,7 @@ const getPartitionSpecificSingleMetricViewerLink = ( }, }); - const hash = `/timeseriesexplorer?${QueryString.encode({ _g, _a })}`; + const hash = `/timeseriesexplorer?${stringify({ _g, _a })}`; return url.format({ pathname, diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index 66bb4308d1d16..fae42d057f4be 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -9,8 +9,7 @@ import throttle from 'lodash/fp/throttle'; import React from 'react'; import { Route, RouteProps } from 'react-router-dom'; import { decode, encode, RisonValue } from 'rison-node'; - -import { QueryString } from 'ui/utils/query_string'; +import { parse, stringify } from 'query-string'; interface UrlStateContainerProps { urlState: UrlState | undefined; @@ -145,7 +144,9 @@ const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (location: Location) => location.search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const queryParam = QueryString.decode(queryString)[key]; + const parsedQueryString: Record = parse(queryString); + const queryParam = parsedQueryString[key]; + return Array.isArray(queryParam) ? queryParam[0] : queryParam; }; @@ -153,13 +154,17 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = QueryString.decode(queryString); + const previousQueryValues = parse(queryString); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return QueryString.encode({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index 79a5d552bcd78..aae57b945791d 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -7,7 +7,7 @@ import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, encode, RisonValue } from 'rison-node'; -import { QueryString } from 'ui/utils/query_string'; +import { parse, stringify } from 'query-string'; import { useHistory } from './history_context'; @@ -100,7 +100,9 @@ const encodeRisonUrlState = (state: any) => encode(state); const getQueryStringFromLocation = (location: Location) => location.search.substring(1); const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const queryParam = QueryString.decode(queryString)[key]; + const parsedQueryString: Record = parse(queryString, { sort: false }); + const queryParam = parsedQueryString[key]; + return Array.isArray(queryParam) ? queryParam[0] : queryParam; }; @@ -108,13 +110,17 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = QueryString.decode(queryString); + const previousQueryValues = parse(queryString, { sort: false }); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return QueryString.encode({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index 4374d424a1175..07a92e9e8486d 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -8,7 +8,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { decode } from 'rison-node'; -import queryString from 'query-string'; +import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; @@ -35,7 +35,7 @@ export const analyticsJobExplorationRoute: MlRoute = { const PageWrapper: FC = ({ location, config, deps }) => { const { context } = useResolver('', undefined, config, basicResolvers(deps)); - const { _g }: Record = queryString.parse(location.search); + const { _g }: Record = parse(location.search, { sort: false }); let globalState: any = null; try { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx index a8e7b07a19b1a..5b4c771b0bec3 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import queryString from 'query-string'; +import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { Page } from '../../../datavisualizer/index_based'; @@ -35,7 +35,7 @@ export const indexBasedRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = queryString.parse(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context } = useResolver(index, savedSearchId, config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx index 3c468a1adea34..4db6d5483b4fc 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx @@ -7,7 +7,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import queryString from 'query-string'; +import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; @@ -32,7 +32,7 @@ export const jobTypeRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = queryString.parse(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context } = useResolver(index, savedSearchId, config, basicResolvers(deps)); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx index 4448c28fd59df..f6e3192a6f1e3 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import queryString from 'query-string'; +import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; @@ -42,7 +42,7 @@ export const checkViewOrCreateRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { id, index, savedSearchId }: Record = queryString.parse(location.search); + const { id, index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), existingJobsAndGroups: mlJobService.getJobAndGroupIds, @@ -56,9 +56,9 @@ const PageWrapper: FC = ({ location, config, deps }) => { }; const CheckViewOrCreateWrapper: FC = ({ location, config, deps }) => { - const { id: moduleId, index: indexPatternId }: Record = queryString.parse( - location.search - ); + const { id: moduleId, index: indexPatternId }: Record = parse(location.search, { + sort: false, + }); // the single resolver checkViewOrCreateJobs redirects only. so will always reject useResolver(undefined, undefined, config, { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index 3c29fdcf0afcd..f0c3283e381cc 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import queryString from 'query-string'; +import { parse } from 'query-string'; import { basicResolvers } from '../../resolvers'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -122,7 +122,7 @@ export const categorizationRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, jobType, deps }) => { - const { index, savedSearchId }: Record = queryString.parse(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), privileges: checkCreateJobsPrivilege, diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx index 4455e6e99ada7..a755d56aea752 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx @@ -8,8 +8,6 @@ import { isEqual } from 'lodash'; import React, { FC, useCallback, useEffect, useState } from 'react'; import { usePrevious } from 'react-use'; import moment from 'moment'; -// @ts-ignore -import queryString from 'query-string'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 5357fc1d85bb4..77408dacec52d 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -6,7 +6,7 @@ import { useCallback } from 'react'; import { isEqual } from 'lodash'; -import queryString from 'query-string'; +import { parse, stringify } from 'query-string'; import { decode, encode } from 'rison-node'; import { useHistory, useLocation } from 'react-router-dom'; @@ -32,7 +32,7 @@ function isRisonSerializationRequired(queryParam: string): boolean { export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; - const parsedQueryString: Record = queryString.parse(search); + const parsedQueryString: Record = parse(search, { sort: false }); try { Object.keys(parsedQueryString).forEach(a => { @@ -63,7 +63,7 @@ export const useUrlState = (accessor: string): UrlState => { const setUrlState = useCallback( (attribute: string | Dictionary, value?: any) => { const urlState = getUrlState(search); - const parsedQueryString = queryString.parse(search); + const parsedQueryString = parse(search, { sort: false }); if (!Object.prototype.hasOwnProperty.call(urlState, accessor)) { urlState[accessor] = {}; @@ -83,7 +83,7 @@ export const useUrlState = (accessor: string): UrlState => { } try { - const oldLocationSearch = queryString.stringify(parsedQueryString, { encode: false }); + const oldLocationSearch = stringify(parsedQueryString, { encode: false, sort: false }); Object.keys(urlState).forEach(a => { if (isRisonSerializationRequired(a)) { @@ -92,11 +92,11 @@ export const useUrlState = (accessor: string): UrlState => { parsedQueryString[a] = urlState[a]; } }); - const newLocationSearch = queryString.stringify(parsedQueryString, { encode: false }); + const newLocationSearch = stringify(parsedQueryString, { encode: false, sort: false }); if (oldLocationSearch !== newLocationSearch) { history.push({ - search: queryString.stringify(parsedQueryString), + search: stringify(parsedQueryString, { sort: false }), }); } } catch (error) { diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js index acf50350761ef..af462bfeffcf5 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return parse(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts index 16f2cad135a28..2a61ec7a4fbcf 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts @@ -5,7 +5,7 @@ */ import { npStart } from 'ui/new_platform'; -import querystring from 'query-string'; +import { stringify } from 'query-string'; const { core } = npStart; @@ -20,7 +20,7 @@ interface JobParams { } export const getReportingJobPath = (exportType: string, jobParams: JobParams) => { - const params = querystring.stringify({ jobParams: rison.encode(jobParams) }); + const params = stringify({ jobParams: rison.encode(jobParams) }, { sort: false }); return `${core.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; }; diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js index a3ed59f10a4c4..c1e396dc0b3eb 100644 --- a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js +++ b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import Joi from 'joi'; -import querystring from 'query-string'; +import { stringify } from 'query-string'; import { callWithRequestFactory } from '../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; @@ -45,7 +45,7 @@ export function registerFieldsForWildcardRoute(server) { const { pattern, meta_fields: metaFields, params } = request.query; // Format call to standard index pattern `fields for wildcard` - const standardRequestQuery = querystring.stringify({ pattern, meta_fields: metaFields }); + const standardRequestQuery = stringify({ pattern, meta_fields: metaFields }, { sort: false }); const standardRequest = { url: `${request.getBasePath()}/api/index_patterns/_fields_for_wildcard?${standardRequestQuery}`, method: 'GET', diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index 8bd97304a7e21..743d99f0c7e61 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; -import { QueryString } from 'ui/utils/query_string'; +import { parse, stringify } from 'query-string'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; @@ -22,6 +22,9 @@ interface QueryStringType { type MlHostConditionalProps = Partial> & { url: string }; +const parseQueryStringType = (value: string) => + (parse(value, { sort: false }) as unknown) as QueryStringType; + export const MlHostConditionalContainer = React.memo(({ url }) => ( (({ exact path={url} render={({ location }) => { - const queryStringDecoded: QueryStringType = QueryString.decode( - location.search.substring(1) - ); + const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; }} /> @@ -47,14 +49,14 @@ export const MlHostConditionalContainer = React.memo(({ params: { hostName }, }, }) => { - const queryStringDecoded: QueryStringType = QueryString.decode( - location.search.substring(1) - ); + const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(hostName)) { - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); + return ( ); @@ -65,12 +67,14 @@ export const MlHostConditionalContainer = React.memo(({ hosts, queryStringDecoded.query || '' ); - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); + return ( ); } else { - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); + return ( > & { url: string }; +const parseQueryStringType = (value: string) => + (parse(value, { sort: false }) as unknown) as QueryStringType; + export const MlNetworkConditionalContainer = React.memo(({ url }) => ( { - const queryStringDecoded: QueryStringType = QueryString.decode( - location.search.substring(1) - ); + const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = QueryString.encode(queryStringDecoded); + + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; }} /> @@ -46,14 +49,14 @@ export const MlNetworkConditionalContainer = React.memo { - const queryStringDecoded: QueryStringType = QueryString.decode( - location.search.substring(1) - ); + const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } + if (emptyEntity(ip)) { - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; } else if (multipleEntities(ip)) { const ips: string[] = getMultipleEntities(ip); @@ -62,10 +65,10 @@ export const MlNetworkConditionalContainer = React.memo; } else { - const reEncoded = QueryString.encode(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; } }} diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 03fdbe2219acc..4baa01f7682b7 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -6,7 +6,7 @@ import { decode, encode } from 'rison-node'; import * as H from 'history'; -import { QueryString } from 'ui/utils/query_string'; +import { stringify, parse } from 'query-string'; import { Query, esFilters } from 'src/plugins/data/public'; import { isEmpty } from 'lodash/fp'; @@ -41,29 +41,39 @@ export const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (search: string) => search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const queryParam = QueryString.decode(queryString)[key]; + const parsedQueryString: Record = parse(queryString, { sort: false }); + const queryParam = parsedQueryString[key]; + return Array.isArray(queryParam) ? queryParam[0] : queryParam; }; export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) => ( queryString: string ): string => { - const previousQueryValues = QueryString.decode(queryString); + const previousQueryValues = parse(queryString); if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; - return QueryString.encode({ - ...previousQueryValues, - }); + + return stringify( + { + ...previousQueryValues, + }, + { sort: false } + ); } // ಠ_ಠ Code was copied from x-pack/legacy/plugins/infra/public/utils/url_state.tsx ಠ_ಠ // Remove this if these utilities are promoted to kibana core const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return QueryString.encode({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; export const replaceQueryStringInLocation = ( diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx index b47e79abead2f..1c97d97fd4efd 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx @@ -86,7 +86,7 @@ export const SnapshotList: React.FunctionComponent(undefined); useEffect(() => { if (search) { - const parsedParams = parse(search.replace(/^\?/, '')); + const parsedParams = parse(search.replace(/^\?/, ''), { sort: false }); const { repository, policy } = parsedParams; if (policy && policy !== filteredPolicy) { diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx index f6f027af261e1..5d9af10426767 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx @@ -44,7 +44,7 @@ export const RepositoryAdd: React.FunctionComponent = ({ if (error) { setSaveError(error); } else { - const { redirect } = parse(search.replace(/^\?/, '')); + const { redirect } = parse(search.replace(/^\?/, ''), { sort: false }); history.push(redirect ? (redirect as string) : `${BASE_PATH}/${section}/${name}`); } }; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index 8e9174a36b129..22306594c4bee 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'query-string'; +import { parse, stringify } from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { UptimeUrlParams, getSupportedUrlParams } from '../lib/helper'; @@ -24,7 +24,12 @@ export const useUrlParams: UptimeUrlParamsHook = () => { } const params = search - ? { ...(qs.parse(search[0] === '?' ? search.slice(1) : search) as Record) } + ? { + ...(parse(search[0] === '?' ? search.slice(1) : search, { sort: false }) as Record< + string, + any + >), + } : {}; return getSupportedUrlParams(params); }; @@ -32,9 +37,9 @@ export const useUrlParams: UptimeUrlParamsHook = () => { const updateUrlParams: UpdateUrlParams = updatedParams => { if (!history || !location) return; const { pathname, search } = location; - const currentParams: Record = qs.parse( - search[0] === '?' ? search.slice(1) : search - ); + const currentParams: Record = parse(search[0] === '?' ? search.slice(1) : search, { + sort: false, + }); const mergedParams = { ...currentParams, ...updatedParams, @@ -42,7 +47,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { history.push({ pathname, - search: qs.stringify( + search: stringify( // drop any parameters that have no value Object.keys(mergedParams).reduce((params, key) => { const value = mergedParams[key]; @@ -53,7 +58,8 @@ export const useUrlParams: UptimeUrlParamsHook = () => { ...params, [key]: value, }; - }, {}) + }, {}), + { sort: false } ), }); }; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts index d0055f56a2a70..a8ce86c4399e2 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'query-string'; +import { stringify } from 'query-string'; import { UptimeUrlParams } from './url_params'; import { CLIENT_DEFAULTS } from '../../../common/constants'; @@ -38,5 +38,5 @@ export const stringifyUrlParams = (params: Partial, ignoreEmpty } }); } - return `?${qs.stringify(params)}`; + return `?${stringify(params, { sort: false })}`; }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index cba76d6750384..c61bf42c8c90e 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'query-string'; +import { stringify } from 'query-string'; import { getApiPath } from '../../lib/helper'; import { APIFn } from './types'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; @@ -25,7 +25,7 @@ export const fetchPingHistogram: APIFn ...(statusFilter && { statusFilter }), ...(filters && { filters }), }; - const urlParams = qs.stringify(params); + const urlParams = stringify(params, { sort: false }); const response = await fetch(`${url}?${urlParams}`); if (!response.ok) { throw new Error(response.statusText); diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index f0bd0097491b9..a5b7b2428d4a4 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import querystring from 'query-string'; +import { stringify } from 'query-string'; import { registerHelpers } from './rollup.test_helpers'; import { INDEX_TO_ROLLUP_MAPPINGS, INDEX_PATTERNS_EXTENSION_BASE_PATH } from './constants'; @@ -37,21 +37,21 @@ export default function({ getService }) { it('"params" is required', async () => { params = { pattern: 'foo' }; - uri = `${BASE_URI}?${querystring.stringify(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('"params" is required'); }); it('"params" must be an object', async () => { params = { pattern: 'foo', params: 'bar' }; - uri = `${BASE_URI}?${querystring.stringify(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('"params" must be an object'); }); it('"params" must be an object that only accepts a "rollup_index" property', async () => { params = { pattern: 'foo', params: JSON.stringify({ someProp: 'bar' }) }; - uri = `${BASE_URI}?${querystring.stringify(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('"someProp" is not allowed'); }); @@ -62,16 +62,19 @@ export default function({ getService }) { params: JSON.stringify({ rollup_index: 'bar' }), meta_fields: 'stringValue', }; - uri = `${BASE_URI}?${querystring.stringify(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('"meta_fields" must be an array'); }); it('should return 404 the rollup index to query does not exist', async () => { - uri = `${BASE_URI}?${querystring.stringify({ - pattern: 'foo', - params: JSON.stringify({ rollup_index: 'bar' }), - })}`; + uri = `${BASE_URI}?${stringify( + { + pattern: 'foo', + params: JSON.stringify({ rollup_index: 'bar' }), + }, + { sort: false } + )}`; ({ body } = await supertest.get(uri).expect(404)); expect(body.message).to.contain('no such index [bar]'); }); @@ -89,7 +92,7 @@ export default function({ getService }) { pattern: indexName, params: JSON.stringify({ rollup_index: rollupIndex }), }; - const uri = `${BASE_URI}?${querystring.stringify(params)}`; + const uri = `${BASE_URI}?${stringify(params, { sort: false })}`; const { body } = await supertest.get(uri).expect(200); // Verify that the fields for wildcard correspond to our declared mappings diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 60d91962cd01c..610850cfb00bb 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import querystring from 'query-string'; +import { stringify } from 'query-string'; import url from 'url'; import { delay } from 'bluebird'; import expect from '@kbn/expect'; @@ -443,7 +443,7 @@ export default function({ getService }: FtrProviderContext) { it('should invalidate access token on IdP initiated logout', async () => { const logoutRequest = await createLogoutRequest({ sessionIndex: idpSessionIndex }); const logoutResponse = await supertest - .get(`/api/security/logout?${querystring.stringify(logoutRequest)}`) + .get(`/api/security/logout?${stringify(logoutRequest, { sort: false })}`) .set('Cookie', sessionCookie.cookieString()) .expect(302); @@ -479,7 +479,7 @@ export default function({ getService }: FtrProviderContext) { it('should invalidate access token on IdP initiated logout even if there is no Kibana session', async () => { const logoutRequest = await createLogoutRequest({ sessionIndex: idpSessionIndex }); const logoutResponse = await supertest - .get(`/api/security/logout?${querystring.stringify(logoutRequest)}`) + .get(`/api/security/logout?${stringify(logoutRequest, { sort: false })}`) .expect(302); expect(logoutResponse.headers['set-cookie']).to.be(undefined); diff --git a/x-pack/test/saml_api_integration/fixtures/saml_tools.ts b/x-pack/test/saml_api_integration/fixtures/saml_tools.ts index 0e59eb53c25d7..bbe0df7ff3a2c 100644 --- a/x-pack/test/saml_api_integration/fixtures/saml_tools.ts +++ b/x-pack/test/saml_api_integration/fixtures/saml_tools.ts @@ -6,7 +6,7 @@ import crypto from 'crypto'; import fs from 'fs'; -import querystring from 'query-string'; +import { stringify } from 'query-string'; import url from 'url'; import zlib from 'zlib'; import { promisify } from 'util'; @@ -140,7 +140,7 @@ export async function getLogoutRequest({ }; const signer = crypto.createSign('RSA-SHA256'); - signer.update(querystring.stringify(queryStringParameters)); + signer.update(stringify(queryStringParameters, { sort: false })); queryStringParameters.Signature = signer.sign(signingKey.toString(), 'base64'); return queryStringParameters; From 94fca786f319be987916fe0322a6158f988b20d7 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 6 Feb 2020 20:08:32 +0300 Subject: [PATCH 03/18] remove query_string file --- src/legacy/ui/public/utils/query_string.js | 28 ----------------- src/legacy/utils/index.js | 1 - src/plugins/kibana_utils/public/index.ts | 1 + .../url/stringify_query_string.ts | 31 ++----------------- .../public/url}/encode_query_component.ts | 1 + .../kibana_utils/public/url/index.ts} | 8 +---- .../workpad_header/workpad_export/utils.ts | 14 ++++----- 7 files changed, 12 insertions(+), 72 deletions(-) delete mode 100644 src/legacy/ui/public/utils/query_string.js rename src/{legacy/utils => plugins/kibana_utils/public/url}/encode_query_component.ts (98%) rename src/{legacy/ui/public/utils/query_string.d.ts => plugins/kibana_utils/public/url/index.ts} (83%) diff --git a/src/legacy/ui/public/utils/query_string.js b/src/legacy/ui/public/utils/query_string.js deleted file mode 100644 index 6964cc7552425..0000000000000 --- a/src/legacy/ui/public/utils/query_string.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you 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 { encodeQueryComponent } from '../../../utils'; - -export const QueryString = {}; - -QueryString.param = function(key, val) { - return ( - encodeQueryComponent(key, true) + (val === true ? '' : '=' + encodeQueryComponent(val, true)) - ); -}; diff --git a/src/legacy/utils/index.js b/src/legacy/utils/index.js index 2e6381b31ecee..a4c0cdf958fc2 100644 --- a/src/legacy/utils/index.js +++ b/src/legacy/utils/index.js @@ -21,7 +21,6 @@ export { BinderBase } from './binder'; export { BinderFor } from './binder_for'; export { deepCloneWithBuffers } from './deep_clone_with_buffers'; export { unset } from './unset'; -export { encodeQueryComponent } from './encode_query_component'; export { watchStdioForLine } from './watch_stdio_for_line'; export { IS_KIBANA_DISTRIBUTABLE } from './artifact_type'; export { IS_KIBANA_RELEASE } from './artifact_type'; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 883f28da45223..a9d29134eaf66 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -70,3 +70,4 @@ export { } from './state_sync'; export { removeQueryParam } from './history'; export { applyDiff } from './state_management/utils/diff_object'; +export { encodeQueryComponent } from './url'; diff --git a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts b/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts index c91a22b4ff08d..c7ae1a9e1c3ec 100644 --- a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts +++ b/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts @@ -18,13 +18,14 @@ */ import { transform } from 'lodash'; import { stringify } from 'query-string'; +import { encodeQueryComponent } from '../../url'; // encodeUriQuery implements the less-aggressive encoding done naturally by // the browser. We use it to generate the same urls the browser would export const stringifyQueryString = (query: Record) => { const encodedQuery = transform(query, (result, value, key) => { if (key && value) { - result[key] = encodeUriQuery(value, true); + result[key] = encodeQueryComponent(value, true); } }); @@ -34,31 +35,3 @@ export const stringifyQueryString = (query: Record) => { sort: false, }); }; - -/** - * Extracted from angular.js - * repo: https://github.com/angular/angular.js - * license: MIT - https://github.com/angular/angular.js/blob/51c516e7d4f2d10b0aaa4487bd0b52772022207a/LICENSE - * source: https://github.com/angular/angular.js/blob/51c516e7d4f2d10b0aaa4487bd0b52772022207a/src/Angular.js#L1413-L1432 - */ - -/** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -export function encodeUriQuery(val: string, pctEncodeSpaces: boolean = false) { - return encodeURIComponent(val) - .replace(/%40/gi, '@') - .replace(/%3A/gi, ':') - .replace(/%24/g, '$') - .replace(/%2C/gi, ',') - .replace(/%3B/gi, ';') - .replace(/%20/g, pctEncodeSpaces ? '%20' : '+'); -} diff --git a/src/legacy/utils/encode_query_component.ts b/src/plugins/kibana_utils/public/url/encode_query_component.ts similarity index 98% rename from src/legacy/utils/encode_query_component.ts rename to src/plugins/kibana_utils/public/url/encode_query_component.ts index 698d11803649d..5b54575653418 100644 --- a/src/legacy/utils/encode_query_component.ts +++ b/src/plugins/kibana_utils/public/url/encode_query_component.ts @@ -34,5 +34,6 @@ export function encodeQueryComponent(val: string, pctEncodeSpaces = false) { .replace(/%3A/gi, ':') .replace(/%24/g, '$') .replace(/%2C/gi, ',') + .replace(/%3B/gi, ';') .replace(/%20/g, pctEncodeSpaces ? '%20' : '+'); } diff --git a/src/legacy/ui/public/utils/query_string.d.ts b/src/plugins/kibana_utils/public/url/index.ts similarity index 83% rename from src/legacy/ui/public/utils/query_string.d.ts rename to src/plugins/kibana_utils/public/url/index.ts index 3f3c1752d38b2..3f7137287c5f0 100644 --- a/src/legacy/ui/public/utils/query_string.d.ts +++ b/src/plugins/kibana_utils/public/url/index.ts @@ -17,10 +17,4 @@ * under the License. */ -declare class QueryStringClass { - public param(key: string, value: string): string; -} - -declare const QueryString: QueryStringClass; - -export { QueryString }; +export { encodeQueryComponent } from './encode_query_component'; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index b385ee74c740f..789cf1f1adbcc 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -// todo: Should be removed -import { QueryString } from 'ui/utils/query_string'; - import rison from 'rison-node'; // @ts-ignore Untyped local. import { fetch } from '../../../../common/lib/fetch'; import { CanvasWorkpad } from '../../../../types'; +import { encodeQueryComponent } from '../../../../../../../../src/plugins/kibana_utils/public'; // type of the desired pdf output (print or preserve_layout) const PDF_LAYOUT_TYPE = 'preserve_layout'; @@ -73,11 +71,13 @@ function getPdfUrlParts( export function getPdfUrl(...args: Arguments): string { const urlParts = getPdfUrlParts(...args); + const param = (key, val) => { + return ( + encodeQueryComponent(key, true) + (val === true ? '' : '=' + encodeQueryComponent(val, true)) + ); + }; - return `${urlParts.createPdfUri}?${QueryString.param( - 'jobParams', - urlParts.createPdfPayload.jobParams - )}`; + return `${urlParts.createPdfUri}?${param('jobParams', urlParts.createPdfPayload.jobParams)}`; } export function createPdf(...args: Arguments) { From f045669aaa628f5f0c1c082a3b4f8731a2106946 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 6 Feb 2020 20:15:36 +0300 Subject: [PATCH 04/18] remove querystring-browser from package.json --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 866adfa4f6caf..62e19817a763d 100644 --- a/package.json +++ b/package.json @@ -230,7 +230,6 @@ "prop-types": "15.6.0", "proxy-from-env": "1.0.0", "pug": "^2.0.4", - "querystring-browser": "1.0.4", "query-string": "6.10.1", "raw-loader": "3.1.0", "react": "^16.12.0", diff --git a/yarn.lock b/yarn.lock index f29215c2f8102..44aa4cf938101 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23803,11 +23803,6 @@ query-string@^5.0.1: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-browser@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" - integrity sha1-8uNYgYQKgZvHsb9Zf68JeeZiLcY= - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" From afdf8ca3f117caab510c1e029cccd989475d8be0 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 7 Feb 2020 14:31:07 +0300 Subject: [PATCH 05/18] add kibana_utils\url module --- src/core/utils/url.ts | 3 +- src/legacy/server/logging/log_format.js | 4 +- .../editor/legacy/console_editor/editor.tsx | 5 +- src/plugins/console/public/lib/es/es.ts | 4 +- .../query/timefilter/lib/parse_querystring.ts | 4 +- src/plugins/kibana_utils/common/index.ts | 1 + .../url/encode_query_component.test.ts} | 35 ++++---------- .../url/encode_query_component.ts | 0 .../{public => common}/url/index.ts | 9 +++- .../url/utils.test.ts} | 32 ++++++------- src/plugins/kibana_utils/common/url/utils.ts | 48 +++++++++++++++++++ .../public/history/remove_query_param.ts | 7 ++- src/plugins/kibana_utils/public/index.ts | 2 +- .../public/state_management/url/format.ts | 8 ++-- .../state_management/url/kbn_url_storage.ts | 6 +-- src/plugins/kibana_utils/server/index.ts | 2 +- .../server/series_functions/quandl.test.js | 8 ++-- .../components/shared/Links/url_helpers.ts | 19 ++------ .../workpad_header/workpad_export/utils.ts | 10 ++-- .../plugins/canvas/public/lib/app_state.ts | 4 +- .../plugins/canvas/public/lib/modify_url.ts | 4 +- .../public/app/services/query_params.js | 4 +- .../public/app/services/routing.js | 8 +--- .../analyze_in_ml_button.tsx | 6 +-- .../plugins/infra/public/utils/url_state.tsx | 17 +++---- .../infra/public/utils/use_url_state.ts | 17 +++---- .../analytics_job_exploration.tsx | 5 +- .../routes/datavisualizer/index_based.tsx | 4 +- .../routing/routes/new_job/job_type.tsx | 6 +-- .../routing/routes/new_job/wizard.tsx | 4 +- .../ml/public/application/util/url_state.ts | 12 ++--- .../public/app/services/query_params.js | 4 +- .../server/lib/encode_uri_query.js | 39 --------------- .../printable_pdf/server/lib/uri_encode.js | 12 ++--- .../reporting/public/lib/reporting_client.ts | 8 ++-- .../server/routes/api/index_patterns.js | 4 +- .../ml_host_conditional_container.tsx | 21 ++++---- .../ml_network_conditional_container.tsx | 23 +++++---- .../home/snapshot_list/snapshot_list.tsx | 4 +- .../repository_add/repository_add.tsx | 5 +- .../uptime/public/hooks/use_url_params.ts | 16 ++----- .../public/lib/helper/stringify_url_params.ts | 4 +- .../plugins/uptime/public/state/api/ping.ts | 4 +- x-pack/plugins/spaces/server/lib/utils/url.ts | 3 +- 44 files changed, 206 insertions(+), 239 deletions(-) rename src/plugins/kibana_utils/{public/state_management/url/stringify_query_string.test.ts => common/url/encode_query_component.test.ts} (56%) rename src/plugins/kibana_utils/{public => common}/url/encode_query_component.ts (100%) rename src/plugins/kibana_utils/{public => common}/url/index.ts (79%) rename src/plugins/kibana_utils/{public/state_management/url/stringify_query_string.ts => common/url/utils.test.ts} (57%) create mode 100644 src/plugins/kibana_utils/common/url/utils.ts delete mode 100644 x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/encode_uri_query.js diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index 869adbc600b84..649ad0abee332 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -17,7 +17,6 @@ * under the License. */ -import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -33,7 +32,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: ParsedQuery; + query: Record; } /** diff --git a/src/legacy/server/logging/log_format.js b/src/legacy/server/logging/log_format.js index 8a02dfe04646d..b3b07ce7802ee 100644 --- a/src/legacy/server/logging/log_format.js +++ b/src/legacy/server/logging/log_format.js @@ -23,10 +23,10 @@ import { get, _ } from 'lodash'; import numeral from '@elastic/numeral'; import chalk from 'chalk'; import stringify from 'json-stringify-safe'; -import querystring from 'query-string'; import applyFiltersToKeys from './apply_filters_to_keys'; import { inspect } from 'util'; import { logWithMetadata } from './log_with_metadata'; +import { url } from '../../../plugins/kibana_utils/server'; function serializeError(err = {}) { return { @@ -108,7 +108,7 @@ export default class TransformObjStream extends Stream.Transform { contentLength: contentLength, }; - const query = querystring.stringify(event.query, { sort: false }); + const query = url.stringifyUrlQuery(event.query); if (query) data.req.url += '?' + query; data.message = data.req.method.toUpperCase() + ' '; diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index c5d060845b50d..958b8c2636d19 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -21,12 +21,11 @@ import React, { CSSProperties, useCallback, useEffect, useRef, useState } from ' import { EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { debounce } from 'lodash'; -import { parse } from 'query-string'; - import { EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useServicesContext, useEditorReadContext } from '../../../../contexts'; import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode'; import { ConsoleMenu } from '../../../../components'; +import { url as urlUtils } from '../../../../../../../../plugins/kibana_utils/public'; import { autoIndent, getDocumentation } from '../console_menu_actions'; import { registerCommands } from './keyboard_shortcuts'; @@ -96,7 +95,7 @@ function EditorUI({ initialTextValue }: EditorProps) { const readQueryParams = () => { const [, queryString] = (window.location.hash || '').split('?'); - return parse(queryString || '', { sort: false }) as Record; + return urlUtils.parseUrlQuery(queryString || ''); }; const loadBufferFromRemote = (url: string) => { diff --git a/src/plugins/console/public/lib/es/es.ts b/src/plugins/console/public/lib/es/es.ts index 7197395368be9..c3f08d0e385a0 100644 --- a/src/plugins/console/public/lib/es/es.ts +++ b/src/plugins/console/public/lib/es/es.ts @@ -17,8 +17,8 @@ * under the License. */ -import { stringify as formatQueryString } from 'query-string'; import $ from 'jquery'; +import { url } from '../../../../kibana_utils/public'; const esVersion: string[] = []; @@ -35,7 +35,7 @@ export function send(method: string, path: string, data: any) { const wrappedDfd = $.Deferred(); // eslint-disable-line new-cap const options: JQuery.AjaxSettings = { - url: '../api/console/proxy?' + formatQueryString({ path, method }), + url: '../api/console/proxy?' + url.stringifyUrlQuery({ path, method }), data, contentType: getContentType(data), cache: false, diff --git a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts index e549ab04995f4..77172dc3e425c 100644 --- a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts +++ b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts @@ -17,7 +17,7 @@ * under the License. */ -import { parse } from 'query-string'; +import { url } from '../../../../../kibana_utils/public'; export function parseQueryString() { // window.location.search is an empty string @@ -27,5 +27,5 @@ export function parseQueryString() { return {}; } - return parse(hrefSplit[1], { sort: false }); + return url.parseUrlQuery(hrefSplit[1]); } diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index fb608a0db1ac2..6383c81f1c40f 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -23,3 +23,4 @@ export * from './ui'; export * from './state_containers'; export { createGetterSetter, Get, Set } from './create_getter_setter'; export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value'; +export { url } from './url'; diff --git a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.test.ts b/src/plugins/kibana_utils/common/url/encode_query_component.test.ts similarity index 56% rename from src/plugins/kibana_utils/public/state_management/url/stringify_query_string.test.ts rename to src/plugins/kibana_utils/common/url/encode_query_component.test.ts index 3ca6cb4214682..228c711e23ad0 100644 --- a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.test.ts +++ b/src/plugins/kibana_utils/common/url/encode_query_component.test.ts @@ -17,49 +17,32 @@ * under the License. */ -import { encodeUriQuery, stringifyQueryString } from './stringify_query_string'; - -describe('stringifyQueryString', () => { - it('stringifyQueryString', () => { - expect( - stringifyQueryString({ - a: 'asdf1234asdf', - b: "-_.!~*'() -_.!~*'()", - c: ':@$, :@$,', - d: "&;=+# &;=+#'", - f: ' ', - g: 'null', - }) - ).toMatchInlineSnapshot( - `"a=asdf1234asdf&b=-_.!~*'()%20-_.!~*'()&c=:@$,%20:@$,&d=%26;%3D%2B%23%20%26;%3D%2B%23'&f=%20&g=null"` - ); - }); -}); +import { encodeQueryComponent } from './encode_query_component'; describe('encodeUriQuery', function() { it('should correctly encode uri query and not encode chars defined as pchar set in rfc3986', () => { // don't encode alphanum - expect(encodeUriQuery('asdf1234asdf')).toBe('asdf1234asdf'); + expect(encodeQueryComponent('asdf1234asdf')).toBe('asdf1234asdf'); // don't encode unreserved - expect(encodeUriQuery("-_.!~*'() -_.!~*'()")).toBe("-_.!~*'()+-_.!~*'()"); + expect(encodeQueryComponent("-_.!~*'() -_.!~*'()")).toBe("-_.!~*'()+-_.!~*'()"); // don't encode the rest of pchar - expect(encodeUriQuery(':@$, :@$,')).toBe(':@$,+:@$,'); + expect(encodeQueryComponent(':@$, :@$,')).toBe(':@$,+:@$,'); // encode '&', ';', '=', '+', and '#' - expect(encodeUriQuery('&;=+# &;=+#')).toBe('%26;%3D%2B%23+%26;%3D%2B%23'); + expect(encodeQueryComponent('&;=+# &;=+#')).toBe('%26;%3D%2B%23+%26;%3D%2B%23'); // encode ' ' as '+' - expect(encodeUriQuery(' ')).toBe('++'); + expect(encodeQueryComponent(' ')).toBe('++'); // encode ' ' as '%20' when a flag is used - expect(encodeUriQuery(' ', true)).toBe('%20%20'); + expect(encodeQueryComponent(' ', true)).toBe('%20%20'); // do not encode `null` as '+' when flag is used - expect(encodeUriQuery('null', true)).toBe('null'); + expect(encodeQueryComponent('null', true)).toBe('null'); // do not encode `null` with no flag - expect(encodeUriQuery('null')).toBe('null'); + expect(encodeQueryComponent('null')).toBe('null'); }); }); diff --git a/src/plugins/kibana_utils/public/url/encode_query_component.ts b/src/plugins/kibana_utils/common/url/encode_query_component.ts similarity index 100% rename from src/plugins/kibana_utils/public/url/encode_query_component.ts rename to src/plugins/kibana_utils/common/url/encode_query_component.ts diff --git a/src/plugins/kibana_utils/public/url/index.ts b/src/plugins/kibana_utils/common/url/index.ts similarity index 79% rename from src/plugins/kibana_utils/public/url/index.ts rename to src/plugins/kibana_utils/common/url/index.ts index 3f7137287c5f0..c7c8f07f00a52 100644 --- a/src/plugins/kibana_utils/public/url/index.ts +++ b/src/plugins/kibana_utils/common/url/index.ts @@ -17,4 +17,11 @@ * under the License. */ -export { encodeQueryComponent } from './encode_query_component'; +import { encodeQueryComponent } from './encode_query_component'; +import { stringifyUrlQuery, parseUrlQuery } from './utils'; + +export const url = { + encodeQueryComponent, + stringifyUrlQuery, + parseUrlQuery, +}; diff --git a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts b/src/plugins/kibana_utils/common/url/utils.test.ts similarity index 57% rename from src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts rename to src/plugins/kibana_utils/common/url/utils.test.ts index c7ae1a9e1c3ec..6bbf6416e12dc 100644 --- a/src/plugins/kibana_utils/public/state_management/url/stringify_query_string.ts +++ b/src/plugins/kibana_utils/common/url/utils.test.ts @@ -16,22 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -import { transform } from 'lodash'; -import { stringify } from 'query-string'; -import { encodeQueryComponent } from '../../url'; -// encodeUriQuery implements the less-aggressive encoding done naturally by -// the browser. We use it to generate the same urls the browser would -export const stringifyQueryString = (query: Record) => { - const encodedQuery = transform(query, (result, value, key) => { - if (key && value) { - result[key] = encodeQueryComponent(value, true); - } - }); +import { stringifyUrlQuery } from './utils'; - return stringify(encodedQuery, { - strict: false, - encode: false, - sort: false, +describe('stringifyUrlQuery', () => { + it('stringifyUrlQuery', () => { + expect( + stringifyUrlQuery({ + a: 'asdf1234asdf', + b: "-_.!~*'() -_.!~*'()", + c: ':@$, :@$,', + d: "&;=+# &;=+#'", + f: ' ', + g: 'null', + }) + ).toBe( + `a=asdf1234asdf&b=-_.!~*'()%20-_.!~*'()&c=:@$,%20:@$,&d=%26;%3D%2B%23%20%26;%3D%2B%23'&f=%20&g=null` + ); }); -}; +}); diff --git a/src/plugins/kibana_utils/common/url/utils.ts b/src/plugins/kibana_utils/common/url/utils.ts new file mode 100644 index 0000000000000..415454fa8b7a7 --- /dev/null +++ b/src/plugins/kibana_utils/common/url/utils.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { transform } from 'lodash'; +import { stringify, parse, StringifyOptions } from 'query-string'; +import { encodeQueryComponent } from './encode_query_component'; + +export const parseUrlQuery = >(val: string): TReturn => { + return (parse(val, { sort: false }) as unknown) as TReturn; +}; + +export const stringifyUrlQuery = ( + query: Record, + encodeFunction: + | false + | ((val: string, pctEncodeSpaces?: boolean) => string) = encodeQueryComponent, + options: StringifyOptions = {} +) => { + const encodedQuery = + encodeFunction && + transform(query, (result, value, key) => { + if (key && value) { + result[key] = encodeFunction(value, true); + } + }); + + return stringify(encodedQuery || query, { + strict: false, + encode: false, + sort: false, + ...options, + }); +}; diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index c4161ef100a3b..4c5e6fee6c9c8 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -18,17 +18,16 @@ */ import { History, Location } from 'history'; -import { parse } from 'query-string'; -import { stringifyQueryString } from '../state_management/url/stringify_query_string'; // TODO: extract it to ../url +import { url } from '../../../kibana_utils/public'; export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; const search = (oldLocation.search || '').replace(/^\?/, ''); - const query = parse(search, { sort: false }); + const query = url.parseUrlQuery(search); delete query[param]; - const newSearch = stringifyQueryString(query); + const newSearch = url.stringifyUrlQuery(query); const newLocation: Location = { ...oldLocation, search: newSearch, diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index a9d29134eaf66..e2895aa9b4e9a 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -26,6 +26,7 @@ export { Set, UiComponent, UiComponentInstance, + url, } from '../common'; export * from './core'; export * from './errors'; @@ -70,4 +71,3 @@ export { } from './state_sync'; export { removeQueryParam } from './history'; export { applyDiff } from './state_management/utils/diff_object'; -export { encodeQueryComponent } from './url'; diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 1247176db8b19..5fdd035f2ff69 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -18,18 +18,18 @@ */ import { format as formatUrl } from 'url'; -import { ParsedQuery } from 'query-string'; import { parseUrl, parseUrlHash } from './parse'; -import { stringifyQueryString } from './stringify_query_string'; +import { url as urlUtils } from '../../../common'; export function replaceUrlHashQuery( rawUrl: string, - queryReplacer: (query: ParsedQuery) => ParsedQuery + queryReplacer: (query: Record) => Record ) { const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); const newQuery = queryReplacer(hash?.query || {}); - const searchQueryString = stringifyQueryString(newQuery); + const searchQueryString = urlUtils.stringifyUrlQuery(newQuery); + if ((!hash || !hash.search) && !searchQueryString) return rawUrl; // nothing to change. return original url return formatUrl({ ...url, diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index 1dd204e717213..d4f4ef90b3e23 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -21,8 +21,8 @@ import { format as formatUrl } from 'url'; import { createBrowserHistory, History } from 'history'; import { decodeState, encodeState } from '../state_encoder'; import { getCurrentUrl, parseUrl, parseUrlHash } from './parse'; -import { stringifyQueryString } from './stringify_query_string'; import { replaceUrlHashQuery } from './format'; +import { url as urlUtils } from '../../../common'; /** * Parses a kibana url and retrieves all the states encoded into url, @@ -243,11 +243,11 @@ export function getRelativeToHistoryPath(absoluteUrl: string, history: History): return formatUrl({ pathname: stripBasename(parsedUrl.pathname), - search: stringifyQueryString(parsedUrl.query), + search: urlUtils.stringifyUrlQuery(parsedUrl.query), hash: parsedHash ? formatUrl({ pathname: parsedHash.pathname, - search: stringifyQueryString(parsedHash.query), + search: urlUtils.stringifyUrlQuery(parsedHash.query), }) : parsedUrl.hash, }); diff --git a/src/plugins/kibana_utils/server/index.ts b/src/plugins/kibana_utils/server/index.ts index f8b79a1b8b339..b8b768da0192e 100644 --- a/src/plugins/kibana_utils/server/index.ts +++ b/src/plugins/kibana_utils/server/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { Get, Set, createGetterSetter } from '../common'; +export { Get, Set, createGetterSetter, url } from '../common'; diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/timelion/server/series_functions/quandl.test.js index 708d9c0d2588d..1722a5404e2dc 100644 --- a/src/plugins/timelion/server/series_functions/quandl.test.js +++ b/src/plugins/timelion/server/series_functions/quandl.test.js @@ -18,15 +18,15 @@ */ import fn from './quandl'; +import { url as urlUtils } from '../../../kibana_utils/server'; +import moment from 'moment'; +import fetchMock from 'node-fetch'; const parseURL = require('url').parse; -const parseQueryString = require('query-string').parse; const tlConfig = require('./fixtures/tl_config')(); -import moment from 'moment'; -import fetchMock from 'node-fetch'; function parseUrlParams(url) { - return parseQueryString(parseURL(url).query, { sort: false }); + return urlUtils.parseUrlQuery(parseURL(url).query); } jest.mock('node-fetch', () => diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index 6fd4960f4e583..08233c3503018 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -3,26 +3,17 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { transform } from 'lodash'; -import { parse, stringify } from 'query-string'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; export function toQuery(search?: string): APMQueryParamsRaw { - return search ? parse(search.slice(1), { sort: false }) : {}; + return search ? url.parseUrlQuery(search.slice(1)) : {}; } export function fromQuery(query: Record) { - const encodedQuery = transform(query, (result, value, key) => { - if (key && value) { - result[key] = encodeURIComponent(value).replace(/%3A/g, ':'); - } - }); - - return stringify(encodedQuery, { - strict: false, - encode: false, - sort: false - }); + return url.stringifyUrlQuery(query, (value: string) => + encodeURIComponent(value).replace(/%3A/g, ':') + ); } export type APMQueryParams = { diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index 789cf1f1adbcc..e14cf70d3b2f7 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -8,7 +8,7 @@ import rison from 'rison-node'; // @ts-ignore Untyped local. import { fetch } from '../../../../common/lib/fetch'; import { CanvasWorkpad } from '../../../../types'; -import { encodeQueryComponent } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; // type of the desired pdf output (print or preserve_layout) const PDF_LAYOUT_TYPE = 'preserve_layout'; @@ -71,11 +71,9 @@ function getPdfUrlParts( export function getPdfUrl(...args: Arguments): string { const urlParts = getPdfUrlParts(...args); - const param = (key, val) => { - return ( - encodeQueryComponent(key, true) + (val === true ? '' : '=' + encodeQueryComponent(val, true)) - ); - }; + const param = (key: string, val: any) => + url.encodeQueryComponent(key, true) + + (val === true ? '' : '=' + url.encodeQueryComponent(val, true)); return `${urlParts.createPdfUri}?${param('jobParams', urlParts.createPdfPayload.jobParams)}`; } diff --git a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts index d431202ba75a4..1f03509f84151 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; import { get } from 'lodash'; // @ts-ignore untyped local import { getInitialState } from '../state/initial_state'; @@ -15,6 +14,7 @@ import { historyProvider } from './history_provider'; import { routerProvider } from './router_provider'; import { createTimeInterval, isValidTimeInterval, getTimeInterval } from './time_interval'; import { AppState, AppStateKeys } from '../../types'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; export function getDefaultAppState(): AppState { const transientState = getInitialState('transient'); @@ -38,7 +38,7 @@ export function getDefaultAppState(): AppState { export function getCurrentAppState(): AppState { const history = historyProvider(getWindow()); const { search } = history.getLocation(); - const qs = !!search ? parse(search.replace(/^\?/, ''), { sort: false }) : {}; + const qs = !!search ? url.parseUrlQuery(search.replace(/^\?/, '')) : {}; const appState = assignAppState({}, qs); return appState; diff --git a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts index d128dc432e9cf..2e0d3b4f2cc7f 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts @@ -3,8 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -20,7 +18,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: ParsedQuery; + query: Record; } /** diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js index af462bfeffcf5..0949583f0c4db 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return parse(hrefSplit[1], { sort: false }); + return url.parseUrlQuery(hrefSplit[1]); } diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js index f156a49204a4a..a665891ba8763 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js @@ -9,7 +9,7 @@ */ import { createLocation } from 'history'; -import { stringify } from 'query-string'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; import { APPS, BASE_PATH, BASE_PATH_REMOTE_CLUSTERS } from '../../../common/constants'; const isModifiedEvent = event => @@ -22,11 +22,7 @@ const queryParamsFromObject = (params, encodeParams = false) => { return; } - const paramsStr = stringify(params, { - encode: encodeParams, - sort: false, - }); - + const paramsStr = url.stringifyUrlQuery(params, encodeParams ? undefined : false); return `?${paramsStr}`; }; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index 19d7c691a5449..4fbfc488522ee 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -8,9 +8,9 @@ import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { encode } from 'rison-node'; -import { stringify } from 'query-string'; import url from 'url'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; export const AnalyzeInMlButton: React.FunctionComponent<{ @@ -61,7 +61,7 @@ const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRang }, }); - const hash = `/explorer?${stringify({ _g })}`; + const hash = `/explorer?${urlUtils.stringifyUrlQuery({ _g })}`; return url.format({ pathname, @@ -94,7 +94,7 @@ const getPartitionSpecificSingleMetricViewerLink = ( }, }); - const hash = `/timeseriesexplorer?${stringify({ _g, _a })}`; + const hash = `/timeseriesexplorer?${urlUtils.stringifyUrlQuery({ _g, _a })}`; return url.format({ pathname, diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index fae42d057f4be..79e8cd8ea12e0 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -9,7 +9,7 @@ import throttle from 'lodash/fp/throttle'; import React from 'react'; import { Route, RouteProps } from 'react-router-dom'; import { decode, encode, RisonValue } from 'rison-node'; -import { parse, stringify } from 'query-string'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; interface UrlStateContainerProps { urlState: UrlState | undefined; @@ -144,7 +144,7 @@ const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (location: Location) => location.search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = parse(queryString); + const parsedQueryString: Record = url.parseUrlQuery(queryString); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -154,17 +154,14 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = parse(queryString); + const previousQueryValues = url.parseUrlQuery(queryString); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return stringify( - { - ...previousQueryValues, - [stateKey]: encodedUrlState, - }, - { sort: false } - ); + return url.stringifyUrlQuery({ + ...previousQueryValues, + [stateKey]: encodedUrlState, + }); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index aae57b945791d..b23375bcdafb4 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -7,7 +7,7 @@ import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, encode, RisonValue } from 'rison-node'; -import { parse, stringify } from 'query-string'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; import { useHistory } from './history_context'; @@ -100,7 +100,7 @@ const encodeRisonUrlState = (state: any) => encode(state); const getQueryStringFromLocation = (location: Location) => location.search.substring(1); const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = parse(queryString, { sort: false }); + const parsedQueryString: Record = url.parseUrlQuery(queryString); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -110,17 +110,14 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = parse(queryString, { sort: false }); + const previousQueryValues = url.parseUrlQuery(queryString); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return stringify( - { - ...previousQueryValues, - [stateKey]: encodedUrlState, - }, - { sort: false } - ); + return url.stringifyUrlQuery({ + ...previousQueryValues, + [stateKey]: encodedUrlState, + }); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index 07a92e9e8486d..311d6ce509249 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -7,8 +7,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { decode } from 'rison-node'; - -import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; @@ -16,6 +14,7 @@ import { Page } from '../../../data_frame_analytics/pages/analytics_exploration' import { ANALYSIS_CONFIG_TYPE } from '../../../data_frame_analytics/common/analytics'; import { DATA_FRAME_TASK_STATE } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; import { ML_BREADCRUMB } from '../../breadcrumbs'; +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -35,7 +34,7 @@ export const analyticsJobExplorationRoute: MlRoute = { const PageWrapper: FC = ({ location, config, deps }) => { const { context } = useResolver('', undefined, config, basicResolvers(deps)); - const { _g }: Record = parse(location.search, { sort: false }); + const { _g }: Record = url.parseUrlQuery(location.search); let globalState: any = null; try { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx index 5b4c771b0bec3..7f0f0f3e8e8c7 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx @@ -6,7 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { Page } from '../../../datavisualizer/index_based'; @@ -16,6 +15,7 @@ import { checkGetJobsPrivilege } from '../../../privilege/check_privilege'; import { loadIndexPatterns } from '../../../util/index_utils'; import { checkMlNodesAvailable } from '../../../ml_nodes_check'; import { DATA_VISUALIZER_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -35,7 +35,7 @@ export const indexBasedRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = parse(location.search, { sort: false }); + const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); const { context } = useResolver(index, savedSearchId, config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx index 4db6d5483b4fc..4e70749db8b50 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx @@ -6,14 +6,12 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; - -import { parse } from 'query-string'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; import { Page } from '../../../jobs/new_job/pages/job_type'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; - +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, @@ -32,7 +30,7 @@ export const jobTypeRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = parse(location.search, { sort: false }); + const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); const { context } = useResolver(index, savedSearchId, config, basicResolvers(deps)); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index f0c3283e381cc..c60a8dfbcb828 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -6,7 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { parse } from 'query-string'; import { basicResolvers } from '../../resolvers'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -17,6 +16,7 @@ import { mlJobService } from '../../../services/job_service'; import { loadNewJobCapabilities } from '../../../services/new_job_capabilities_service'; import { checkCreateJobsPrivilege } from '../../../privilege/check_privilege'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; interface WizardPageProps extends PageProps { jobType: JOB_TYPE; @@ -122,7 +122,7 @@ export const categorizationRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, jobType, deps }) => { - const { index, savedSearchId }: Record = parse(location.search, { sort: false }); + const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), privileges: checkCreateJobsPrivilege, diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 77408dacec52d..63dd92a96cc1f 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -6,9 +6,9 @@ import { useCallback } from 'react'; import { isEqual } from 'lodash'; -import { parse, stringify } from 'query-string'; import { decode, encode } from 'rison-node'; import { useHistory, useLocation } from 'react-router-dom'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; import { Dictionary } from '../../../common/types/common'; @@ -32,7 +32,7 @@ function isRisonSerializationRequired(queryParam: string): boolean { export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; - const parsedQueryString: Record = parse(search, { sort: false }); + const parsedQueryString: Record = url.parseUrlQuery(search); try { Object.keys(parsedQueryString).forEach(a => { @@ -63,7 +63,7 @@ export const useUrlState = (accessor: string): UrlState => { const setUrlState = useCallback( (attribute: string | Dictionary, value?: any) => { const urlState = getUrlState(search); - const parsedQueryString = parse(search, { sort: false }); + const parsedQueryString = url.parseUrlQuery(search); if (!Object.prototype.hasOwnProperty.call(urlState, accessor)) { urlState[accessor] = {}; @@ -83,7 +83,7 @@ export const useUrlState = (accessor: string): UrlState => { } try { - const oldLocationSearch = stringify(parsedQueryString, { encode: false, sort: false }); + const oldLocationSearch = url.stringifyUrlQuery(parsedQueryString, false); Object.keys(urlState).forEach(a => { if (isRisonSerializationRequired(a)) { @@ -92,11 +92,11 @@ export const useUrlState = (accessor: string): UrlState => { parsedQueryString[a] = urlState[a]; } }); - const newLocationSearch = stringify(parsedQueryString, { encode: false, sort: false }); + const newLocationSearch = url.stringifyUrlQuery(parsedQueryString, false); if (oldLocationSearch !== newLocationSearch) { history.push({ - search: stringify(parsedQueryString, { sort: false }), + search: url.stringifyUrlQuery(parsedQueryString), }); } } catch (error) { diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js index af462bfeffcf5..0949583f0c4db 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return parse(hrefSplit[1], { sort: false }); + return url.parseUrlQuery(hrefSplit[1]); } diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/encode_uri_query.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/encode_uri_query.js deleted file mode 100644 index ce2346b0f28dc..0000000000000 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/encode_uri_query.js +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable @kbn/eslint/require-license-header */ - -// This function was extracted from angular v1.3 - -/* @notice - * This product includes code that was extracted from angular@1.3. - * Original license: - * The MIT License - * - * Copyright (c) 2010-2014 Google, Inc. http://angularjs.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -export function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val) - .replace(/%40/gi, '@') - .replace(/%3A/gi, ':') - .replace(/%24/g, '$') - .replace(/%2C/gi, ',') - .replace(/%3B/gi, ';') - .replace(/%20/g, pctEncodeSpaces ? '%20' : '+'); -} diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js index 5b93461bfaffb..049ac778eb058 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js @@ -5,20 +5,20 @@ */ import { forEach, isArray } from 'lodash'; -import { encodeUriQuery } from './encode_uri_query'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; function toKeyValue(obj) { const parts = []; forEach(obj, function(value, key) { if (isArray(value)) { forEach(value, function(arrayValue) { - const keyStr = encodeUriQuery(key, true); - const valStr = arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true); + const keyStr = url.encodeQueryComponent(key, true); + const valStr = arrayValue === true ? '' : '=' + url.encodeQueryComponent(arrayValue, true); parts.push(keyStr + valStr); }); } else { - const keyStr = encodeUriQuery(key, true); - const valStr = value === true ? '' : '=' + encodeUriQuery(value, true); + const keyStr = url.encodeQueryComponent(key, true); + const valStr = value === true ? '' : '=' + url.encodeQueryComponent(value, true); parts.push(keyStr + valStr); } }); @@ -27,5 +27,5 @@ function toKeyValue(obj) { export const uriEncode = { stringify: toKeyValue, - string: encodeUriQuery, + string: url.encodeQueryComponent, }; diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts index 2a61ec7a4fbcf..12b8471e758c2 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts @@ -5,14 +5,12 @@ */ import { npStart } from 'ui/new_platform'; -import { stringify } from 'query-string'; - -const { core } = npStart; - // @ts-ignore import rison from 'rison-node'; import { add } from './job_completion_notifications'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; +const { core } = npStart; const API_BASE_URL = '/api/reporting/generate'; interface JobParams { @@ -20,7 +18,7 @@ interface JobParams { } export const getReportingJobPath = (exportType: string, jobParams: JobParams) => { - const params = stringify({ jobParams: rison.encode(jobParams) }, { sort: false }); + const params = url.stringifyUrlQuery({ jobParams: rison.encode(jobParams) }); return `${core.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; }; diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js index c1e396dc0b3eb..95f51e0638aed 100644 --- a/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js +++ b/x-pack/legacy/plugins/rollup/server/routes/api/index_patterns.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import Joi from 'joi'; -import { stringify } from 'query-string'; import { callWithRequestFactory } from '../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../lib/error_wrappers'; @@ -12,6 +11,7 @@ import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory' import indexBy from 'lodash/collection/indexBy'; import { getCapabilitiesForRollupIndices } from '../../lib/map_capabilities'; import { mergeCapabilitiesWithFields } from '../../lib/merge_capabilities_with_fields'; +import { url } from '../../../../../../../src/plugins/kibana_utils/server'; /** * Get list of fields for rollup index pattern, in the format of regular index pattern fields @@ -45,7 +45,7 @@ export function registerFieldsForWildcardRoute(server) { const { pattern, meta_fields: metaFields, params } = request.query; // Format call to standard index pattern `fields for wildcard` - const standardRequestQuery = stringify({ pattern, meta_fields: metaFields }, { sort: false }); + const standardRequestQuery = url.stringifyUrlQuery({ pattern, meta_fields: metaFields }); const standardRequest = { url: `${request.getBasePath()}/api/index_patterns/_fields_for_wildcard?${standardRequestQuery}`, method: 'GET', diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index 743d99f0c7e61..f87748909add6 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; -import { parse, stringify } from 'query-string'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; import { SiemPageName } from '../../../pages/home/types'; import { HostsTableType } from '../../../store/hosts/model'; +import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; interface QueryStringType { '?_g': string; @@ -22,9 +22,6 @@ interface QueryStringType { type MlHostConditionalProps = Partial> & { url: string }; -const parseQueryStringType = (value: string) => - (parse(value, { sort: false }) as unknown) as QueryStringType; - export const MlHostConditionalContainer = React.memo(({ url }) => ( (({ exact path={url} render={({ location }) => { - const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + const queryStringDecoded = urlUtils.parseUrlQuery( + location.search.substring(1) + ); if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); return ; }} /> @@ -49,13 +48,15 @@ export const MlHostConditionalContainer = React.memo(({ params: { hostName }, }, }) => { - const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + const queryStringDecoded = urlUtils.parseUrlQuery( + location.search.substring(1) + ); if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(hostName)) { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); return ( @@ -67,13 +68,13 @@ export const MlHostConditionalContainer = React.memo(({ hosts, queryStringDecoded.query || '' ); - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); return ( ); } else { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); return ( > & { url: string }; -const parseQueryStringType = (value: string) => - (parse(value, { sort: false }) as unknown) as QueryStringType; - export const MlNetworkConditionalContainer = React.memo(({ url }) => ( { - const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + const queryStringDecoded = urlUtils.parseUrlQuery( + location.search.substring(1) + ); if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + return ; }} /> @@ -49,14 +49,17 @@ export const MlNetworkConditionalContainer = React.memo { - const queryStringDecoded = parseQueryStringType(location.search.substring(1)); + const queryStringDecoded = urlUtils.parseUrlQuery( + location.search.substring(1) + ); if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(ip)) { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + return ; } else if (multipleEntities(ip)) { const ips: string[] = getMultipleEntities(ip); @@ -65,10 +68,10 @@ export const MlNetworkConditionalContainer = React.memo; } else { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); return ; } }} diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx index 1c97d97fd4efd..5837b8746c9ff 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx @@ -6,7 +6,6 @@ import React, { Fragment, useState, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { parse } from 'query-string'; import { EuiButton, EuiCallOut, EuiLink, EuiEmptyPrompt, EuiSpacer, EuiIcon } from '@elastic/eui'; import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common/constants'; @@ -24,6 +23,7 @@ import { linkToSnapshot, } from '../../../services/navigation'; import { uiMetricService } from '../../../services/ui_metric'; +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; import { SnapshotDetails } from './snapshot_details'; import { SnapshotTable } from './snapshot_table'; @@ -86,7 +86,7 @@ export const SnapshotList: React.FunctionComponent(undefined); useEffect(() => { if (search) { - const parsedParams = parse(search.replace(/^\?/, ''), { sort: false }); + const parsedParams = url.parseUrlQuery(search.replace(/^\?/, '')); const { repository, policy } = parsedParams; if (policy && policy !== filteredPolicy) { diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx index 5d9af10426767..15f2625944f3d 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx @@ -5,7 +5,6 @@ */ import React, { useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { parse } from 'query-string'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; @@ -15,6 +14,7 @@ import { BASE_PATH, Section } from '../../constants'; import { useAppDependencies } from '../../index'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { addRepository } from '../../services/http'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; export const RepositoryAdd: React.FunctionComponent = ({ history, @@ -44,7 +44,8 @@ export const RepositoryAdd: React.FunctionComponent = ({ if (error) { setSaveError(error); } else { - const { redirect } = parse(search.replace(/^\?/, ''), { sort: false }); + const { redirect } = url.parseUrlQuery(search.replace(/^\?/, '')); + history.push(redirect ? (redirect as string) : `${BASE_PATH}/${section}/${name}`); } }; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index 22306594c4bee..be0173644f21a 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse, stringify } from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { UptimeUrlParams, getSupportedUrlParams } from '../lib/helper'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; type GetUrlParams = () => UptimeUrlParams; type UpdateUrlParams = (updatedParams: { [key: string]: string | number | boolean }) => void; @@ -25,10 +25,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { const params = search ? { - ...(parse(search[0] === '?' ? search.slice(1) : search, { sort: false }) as Record< - string, - any - >), + ...url.parseUrlQuery(search[0] === '?' ? search.slice(1) : search), } : {}; return getSupportedUrlParams(params); @@ -37,9 +34,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { const updateUrlParams: UpdateUrlParams = updatedParams => { if (!history || !location) return; const { pathname, search } = location; - const currentParams: Record = parse(search[0] === '?' ? search.slice(1) : search, { - sort: false, - }); + const currentParams = url.parseUrlQuery(search[0] === '?' ? search.slice(1) : search); const mergedParams = { ...currentParams, ...updatedParams, @@ -47,7 +42,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { history.push({ pathname, - search: stringify( + search: url.stringifyUrlQuery( // drop any parameters that have no value Object.keys(mergedParams).reduce((params, key) => { const value = mergedParams[key]; @@ -58,8 +53,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { ...params, [key]: value, }; - }, {}), - { sort: false } + }, {}) ), }); }; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts index a8ce86c4399e2..fd265cf417d82 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { stringify } from 'query-string'; import { UptimeUrlParams } from './url_params'; import { CLIENT_DEFAULTS } from '../../../common/constants'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; const { AUTOREFRESH_INTERVAL, @@ -38,5 +38,5 @@ export const stringifyUrlParams = (params: Partial, ignoreEmpty } }); } - return `?${stringify(params, { sort: false })}`; + return `?${url.stringifyUrlQuery(params)}`; }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index c61bf42c8c90e..7e7cb0ef804e3 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { stringify } from 'query-string'; import { getApiPath } from '../../lib/helper'; import { APIFn } from './types'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; +import { url as urlUtils } from '../../../../../../../src/plugins/kibana_utils/public'; export const fetchPingHistogram: APIFn = async ({ basePath, @@ -25,7 +25,7 @@ export const fetchPingHistogram: APIFn ...(statusFilter && { statusFilter }), ...(filters && { filters }), }; - const urlParams = stringify(params, { sort: false }); + const urlParams = urlUtils.stringifyUrlQuery(params); const response = await fetch(`${url}?${urlParams}`); if (!response.ok) { throw new Error(response.statusText); diff --git a/x-pack/plugins/spaces/server/lib/utils/url.ts b/x-pack/plugins/spaces/server/lib/utils/url.ts index c91934bb99f1f..47dee87686ddf 100644 --- a/x-pack/plugins/spaces/server/lib/utils/url.ts +++ b/x-pack/plugins/spaces/server/lib/utils/url.ts @@ -8,7 +8,6 @@ // DIRECT COPY FROM `src/core/utils/url`, since it's not possible to import from there, // nor can I re-export from `src/core/server`... -import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; export interface URLMeaningfulParts { @@ -19,7 +18,7 @@ export interface URLMeaningfulParts { protocol: string | null; slashes: boolean | null; port: string | null; - query: ParsedQuery | {}; + query: Record | {}; } /** From d6731054aa81f4de8d0a9fd51115303ed6ae0794 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 7 Feb 2020 14:49:52 +0300 Subject: [PATCH 06/18] cleanup --- src/plugins/kibana_utils/common/url/utils.ts | 2 +- .../public/containers/with_url_state.tsx | 19 ++++++-------- .../containers/with_state_from_location.tsx | 8 +++--- .../routing/routes/new_job/recognize.tsx | 9 +++---- .../public/components/url_state/helpers.ts | 26 +++++++------------ .../endpoint/store/alerts/middleware.ts | 4 +-- 6 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/plugins/kibana_utils/common/url/utils.ts b/src/plugins/kibana_utils/common/url/utils.ts index 415454fa8b7a7..7c9ba8d64401e 100644 --- a/src/plugins/kibana_utils/common/url/utils.ts +++ b/src/plugins/kibana_utils/common/url/utils.ts @@ -34,7 +34,7 @@ export const stringifyUrlQuery = ( const encodedQuery = encodeFunction && transform(query, (result, value, key) => { - if (key && value) { + if (key) { result[key] = encodeFunction(value, true); } }); diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx index c8f756da985a7..4056e94b6d529 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx +++ b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse, stringify } from 'query-string'; import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { FlatObject } from '../frontend_types'; import { RendererFunction } from '../utils/typed_react'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; type StateCallback = (previousState: T) => T; @@ -31,9 +31,9 @@ export class WithURLStateComponent extends React.Compon > { private get URLState(): URLState { // slice because parse does not account for the initial ? in the search string - return parse(decodeURIComponent(this.props.history.location.search).substring(1), { - sort: false, - }) as URLState; + return url.parseUrlQuery( + decodeURIComponent(this.props.history.location.search).substring(1) + ); } private historyListener: (() => void) | null = null; @@ -65,13 +65,10 @@ export class WithURLStateComponent extends React.Compon newState = state; } - const search: string = stringify( - { - ...pastState, - ...newState, - }, - { sort: false } - ); + const search: string = url.stringifyUrlQuery({ + ...pastState, + ...newState, + }); const newLocation = { ...this.props.history.location, diff --git a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx index ec6345c49c303..633ea62994c92 100644 --- a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx @@ -6,12 +6,12 @@ import { Location } from 'history'; import omit from 'lodash/fp/omit'; -import { parse as parseQueryString, stringify as stringifyQueryString } from 'querystring'; import React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; // eslint-disable-next-line @typescript-eslint/camelcase import { decode_object, encode_object } from 'rison-node'; import { Omit } from '../lib/lib'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; interface AnyObject { [key: string]: any; @@ -102,7 +102,7 @@ const encodeRisonAppState = (state: AnyObject) => ({ export const mapRisonAppLocationToState = ( mapState: (risonAppState: AnyObject) => State = (state: AnyObject) => state as State ) => (location: Location): State => { - const queryValues = parseQueryString(location.search.substring(1)); + const queryValues = url.parseUrlQuery(location.search.substring(1)); const decodedState = decodeRisonAppState(queryValues); return mapState(decodedState); }; @@ -110,14 +110,14 @@ export const mapRisonAppLocationToState = ( export const mapStateToRisonAppLocation = ( mapState: (state: State) => AnyObject = (state: State) => state ) => (state: State, location: Location): Location => { - const previousQueryValues = parseQueryString(location.search.substring(1)); + const previousQueryValues = url.parseUrlQuery(location.search.substring(1)); const previousState = decodeRisonAppState(previousQueryValues); const encodedState = encodeRisonAppState({ ...previousState, ...mapState(state), }); - const newQueryValues = stringifyQueryString({ + const newQueryValues = url.stringifyUrlQuery({ ...previousQueryValues, ...encodedState, }); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx index f6e3192a6f1e3..e967983cfd0b7 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx @@ -6,8 +6,6 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { parse } from 'query-string'; - import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; @@ -15,6 +13,7 @@ import { Page } from '../../../jobs/new_job/recognize'; import { checkViewOrCreateJobs } from '../../../jobs/new_job/recognize/resolvers'; import { mlJobService } from '../../../services/job_service'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; +import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -42,7 +41,7 @@ export const checkViewOrCreateRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { id, index, savedSearchId }: Record = parse(location.search, { sort: false }); + const { id, index, savedSearchId } = url.parseUrlQuery(location.search); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), existingJobsAndGroups: mlJobService.getJobAndGroupIds, @@ -56,9 +55,7 @@ const PageWrapper: FC = ({ location, config, deps }) => { }; const CheckViewOrCreateWrapper: FC = ({ location, config, deps }) => { - const { id: moduleId, index: indexPatternId }: Record = parse(location.search, { - sort: false, - }); + const { id: moduleId, index: indexPatternId } = url.parseUrlQuery(location.search); // the single resolver checkViewOrCreateJobs redirects only. so will always reject useResolver(undefined, undefined, config, { diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 4baa01f7682b7..e4b8ff7ed9988 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -6,7 +6,6 @@ import { decode, encode } from 'rison-node'; import * as H from 'history'; -import { stringify, parse } from 'query-string'; import { Query, esFilters } from 'src/plugins/data/public'; import { isEmpty } from 'lodash/fp'; @@ -23,6 +22,7 @@ import { Timeline, UpdateUrlStateString, } from './types'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; export const decodeRisonUrlState = (value: string | undefined): T | null => { try { @@ -41,7 +41,7 @@ export const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (search: string) => search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = parse(queryString, { sort: false }); + const parsedQueryString = url.parseUrlQuery(queryString); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -50,16 +50,13 @@ export const getParamFromQueryString = (queryString: string, key: string): strin export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) => ( queryString: string ): string => { - const previousQueryValues = parse(queryString); + const previousQueryValues = url.parseUrlQuery(queryString); if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; - return stringify( - { - ...previousQueryValues, - }, - { sort: false } - ); + return url.stringifyUrlQuery({ + ...previousQueryValues, + }); } // ಠ_ಠ Code was copied from x-pack/legacy/plugins/infra/public/utils/url_state.tsx ಠ_ಠ @@ -67,13 +64,10 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return stringify( - { - ...previousQueryValues, - [stateKey]: encodedUrlState, - }, - { sort: false } - ); + return url.stringifyUrlQuery({ + ...previousQueryValues, + [stateKey]: encodedUrlState, + }); }; export const replaceQueryStringInLocation = ( diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index aede95ceb3759..5eb856c7f9ead 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import qs from 'querystring'; import { HttpFetchQuery } from 'src/core/public'; import { AppAction } from '../action'; import { MiddlewareFactory } from '../../types'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; export const alertMiddlewareFactory: MiddlewareFactory = coreStart => { - const qp = qs.parse(window.location.search.slice(1)); + const qp = url.parseUrlQuery(window.location.search.slice(1)); return api => next => async (action: AppAction) => { next(action); From 38abc365349b57f5372a0b199afc9d1d2b0a691f Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 7 Feb 2020 18:11:08 +0300 Subject: [PATCH 07/18] update notice.txt --- NOTICE.txt | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 69be6db72cff2..33c1d535d7df3 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -218,28 +218,3 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---- -This product includes code that was extracted from angular@1.3. -Original license: -The MIT License - -Copyright (c) 2010-2014 Google, Inc. http://angularjs.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - From 261c76d1c9ec581cd2d18eccc0a764d6e7ed6704 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Sun, 9 Feb 2020 13:39:11 +0300 Subject: [PATCH 08/18] fix merge conflict --- x-pack/legacy/plugins/canvas/public/legacy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index cba683a12aeed..0d5a62939be0f 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -12,7 +12,7 @@ import chrome, { loadingCount } from 'ui/chrome'; // eslint-disable-line import/ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-disable-line import/order import { Storage } from '../../../../../src/plugins/kibana_utils/public'; // eslint-disable-line import/order // @ts-ignore Untyped Kibana Lib -import { formatMsg } from 'ui/notify/lib/format_msg'; // eslint-disable-line import/order +import { formatMsg } from '../../../../../src/plugins/kibana_legacy/public'; // eslint-disable-line import/order const shimCoreSetup = { ...npSetup.core, From b6a12a70a728eeced127f25646fd16b0a5b7e67f Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 10 Feb 2020 12:08:45 +0300 Subject: [PATCH 09/18] fix CI --- src/plugins/kibana_utils/common/url/utils.ts | 4 ++-- .../public/history/remove_query_param.ts | 2 +- .../components/shared/Links/url_helpers.test.tsx | 2 +- .../public/pages/link_to/redirect_to_logs.test.tsx | 6 +++--- .../pages/link_to/redirect_to_node_logs.test.tsx | 12 ++++++------ .../legacy/plugins/infra/public/utils/url_state.tsx | 2 +- .../ml/public/application/util/url_state.test.ts | 3 +-- .../components/url_state/index_mocked.test.tsx | 2 +- .../__snapshots__/stringify_url_params.test.ts.snap | 4 ++-- 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/plugins/kibana_utils/common/url/utils.ts b/src/plugins/kibana_utils/common/url/utils.ts index 7c9ba8d64401e..a4d01fa2454ca 100644 --- a/src/plugins/kibana_utils/common/url/utils.ts +++ b/src/plugins/kibana_utils/common/url/utils.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { transform } from 'lodash'; +import { transform, isUndefined } from 'lodash'; import { stringify, parse, StringifyOptions } from 'query-string'; import { encodeQueryComponent } from './encode_query_component'; @@ -35,7 +35,7 @@ export const stringifyUrlQuery = ( encodeFunction && transform(query, (result, value, key) => { if (key) { - result[key] = encodeFunction(value, true); + result[key] = encodeFunction(isUndefined(value) ? '' : value, true); } }); diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index 4c5e6fee6c9c8..fe46ce4168538 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -18,7 +18,7 @@ */ import { History, Location } from 'history'; -import { url } from '../../../kibana_utils/public'; +import { url } from '../../common'; export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx index f59f3fbab9f79..ac728e72fa877 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx @@ -43,7 +43,7 @@ describe('fromQuery', () => { refreshPaused: true, refreshInterval: 5000 }) - ).toEqual('refreshPaused=true&refreshInterval=5000'); + ).toEqual('flyoutDetailTab=&refreshPaused=true&refreshInterval=5000'); }); }); diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx index a418be01d1ed2..e9ec053f8c609 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx @@ -19,7 +19,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -33,7 +33,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -45,7 +45,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx index 2d1f3a32988aa..1e97072cac109 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx @@ -35,7 +35,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -47,7 +47,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -59,7 +59,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -73,7 +73,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -89,7 +89,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -103,7 +103,7 @@ describe('RedirectToNodeLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index 79e8cd8ea12e0..6307a43519e65 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -144,7 +144,7 @@ const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (location: Location) => location.search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = url.parseUrlQuery(queryString); + const parsedQueryString = url.parseUrlQuery(queryString); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts index 0813f2e3da97f..44fe4588fc7de 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts @@ -74,8 +74,7 @@ describe('useUrlState', () => { }); expect(mockHistoryPush).toHaveBeenCalledWith({ - search: - '_a=%28mlExplorerFilter%3A%28%29%2CmlExplorerSwimlane%3A%28viewByFieldName%3Aaction%29%2Cquery%3A%28%29%29&_g=%28ml%3A%28jobIds%3A%21%28dec-2%29%29%2CrefreshInterval%3A%28display%3AOff%2Cpause%3A%21f%2Cvalue%3A0%29%2Ctime%3A%28from%3A%272019-01-01T00%3A03%3A40.000Z%27%2Cmode%3Aabsolute%2Cto%3A%272019-08-30T11%3A55%3A07.000Z%27%29%29&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d', + search: `_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:())&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d`, }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx index 6995bc8bf1d40..4adc17b32e189 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx @@ -147,7 +147,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - '?timeline=(id:hello_timeline_id,isOpen:!t)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))&timeline=(id:hello_timeline_id,isOpen:!t)', state: '', }); }); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap index 31f5ceff7d046..3519937a49956 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`stringifyUrlParams creates expected string value 1`] = `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id%3A%20bar&search=monitor.id%3A%20foo&selectedPingStatus=down&statusFilter=up"`; +exports[`stringifyUrlParams creates expected string value 1`] = `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id:%20bar&search=monitor.id:%20foo&selectedPingStatus=down&statusFilter=up"`; -exports[`stringifyUrlParams creates expected string value when ignore empty is true 1`] = `"?autorefreshInterval=50000&filters=monitor.id%3A%20bar"`; +exports[`stringifyUrlParams creates expected string value when ignore empty is true 1`] = `"?autorefreshInterval=50000&filters=monitor.id:%20bar"`; From 1b37328d4eeb90ffa642d07604e1c5738a9fcae7 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 10 Feb 2020 15:37:50 +0300 Subject: [PATCH 10/18] fix wrong import --- .../export_types/printable_pdf/server/lib/uri_encode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js index 049ac778eb058..005e6c9ff5c1e 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js @@ -5,7 +5,7 @@ */ import { forEach, isArray } from 'lodash'; -import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { url } from '../../../../../../../../src/plugins/kibana_utils/server'; function toKeyValue(obj) { const parts = []; From bc7d36001c71156e230395ad1d90bb7dd62a3043 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 10 Feb 2020 17:44:33 +0300 Subject: [PATCH 11/18] fix CI --- .../rollup/index_patterns_extensions.js | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index 43d23c71ec8eb..1de944ebac154 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; +import { stringify } from 'query-string'; import { registerHelpers } from './rollup.test_helpers'; import { INDEX_TO_ROLLUP_MAPPINGS, INDEX_PATTERNS_EXTENSION_BASE_PATH } from './constants'; import { getRandomString } from './lib'; @@ -38,7 +38,7 @@ export default function({ getService }) { it('"params" is required', async () => { params = { pattern: 'foo' }; - uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain( '[request query.params]: expected value of type [string]' @@ -47,14 +47,14 @@ export default function({ getService }) { it('"params" must be a valid JSON string', async () => { params = { pattern: 'foo', params: 'foobarbaz' }; - uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('[request query.params]: expected JSON string'); }); it('"params" requires a "rollup_index" property', async () => { params = { pattern: 'foo', params: JSON.stringify({}) }; - uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('[request query.params]: "rollup_index" is required'); }); @@ -64,7 +64,7 @@ export default function({ getService }) { pattern: 'foo', params: JSON.stringify({ rollup_index: 'my_index', someProp: 'bar' }), }; - uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain('[request query.params]: someProp is not allowed'); }); @@ -75,7 +75,7 @@ export default function({ getService }) { params: JSON.stringify({ rollup_index: 'bar' }), meta_fields: 'stringValue', }; - uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + uri = `${BASE_URI}?${stringify(params, { sort: false })}`; ({ body } = await supertest.get(uri).expect(400)); expect(body.message).to.contain( '[request query.meta_fields]: expected value of type [array]' @@ -83,10 +83,13 @@ export default function({ getService }) { }); it('should return 404 the rollup index to query does not exist', async () => { - uri = `${BASE_URI}?${url.stringifyUrlQuery({ - pattern: 'foo', - params: JSON.stringify({ rollup_index: 'bar' }), - })}`; + uri = `${BASE_URI}?${stringify( + { + pattern: 'foo', + params: JSON.stringify({ rollup_index: 'bar' }), + }, + { sort: false } + )}`; ({ body } = await supertest.get(uri).expect(404)); expect(body.message).to.contain('[index_not_found_exception] no such index [bar]'); }); @@ -104,7 +107,7 @@ export default function({ getService }) { pattern: indexName, params: JSON.stringify({ rollup_index: rollupIndex }), }; - const uri = `${BASE_URI}?${url.stringifyUrlQuery(params)}`; + const uri = `${BASE_URI}?${stringify(params, { sort: false })}`; const { body } = await supertest.get(uri).expect(200); // Verify that the fields for wildcard correspond to our declared mappings From b5d6294ba96eb645ccf213298434ca926e8a8765 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 10 Feb 2020 18:52:50 +0300 Subject: [PATCH 12/18] fix X-Pack firefox smoke test --- x-pack/test/functional/apps/infra/link_to.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 639b65ec5eca8..738dc7efd8fd9 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -22,7 +22,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { state: undefined, }; const expectedSearchString = - "logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194),streamLive:!f)&sourceId=default"; + "sourceId=default&logPosition=(position:(tiebreaker:0,time:1565707203194),streamLive:!f)&logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)"; const expectedRedirectPath = '/logs/stream?'; await pageObjects.common.navigateToActualUrl( From 7c1d1f2452a8d9719ce866c3aa50ed326c2c3716 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 11 Feb 2020 12:55:57 +0300 Subject: [PATCH 13/18] remove urlUtils.parseUrlQuery --- .../editor/legacy/console_editor/editor.tsx | 6 +++--- .../query/timefilter/lib/parse_querystring.ts | 5 ++--- src/plugins/kibana_utils/common/url/index.ts | 3 +-- src/plugins/kibana_utils/common/url/utils.ts | 6 +----- .../public/history/remove_query_param.ts | 3 ++- .../timelion/server/series_functions/quandl.test.js | 4 ++-- .../public/components/shared/Links/url_helpers.ts | 4 +++- .../public/containers/with_url_state.tsx | 7 ++++--- .../legacy/plugins/canvas/public/lib/app_state.ts | 4 ++-- .../public/app/services/query_params.js | 4 ++-- .../public/containers/with_state_from_location.tsx | 5 +++-- .../legacy/plugins/infra/public/utils/url_state.tsx | 5 +++-- .../plugins/infra/public/utils/use_url_state.ts | 5 +++-- .../analytics_job_exploration.tsx | 4 ++-- .../routing/routes/datavisualizer/index_based.tsx | 4 ++-- .../application/routing/routes/new_job/job_type.tsx | 5 +++-- .../routing/routes/new_job/recognize.tsx | 8 +++++--- .../application/routing/routes/new_job/wizard.tsx | 4 ++-- .../plugins/ml/public/application/util/url_state.ts | 5 +++-- .../public/app/services/query_params.js | 4 ++-- .../ml_host_conditional_container.tsx | 13 +++++++------ .../ml_network_conditional_container.tsx | 13 +++++++------ .../siem/public/components/url_state/helpers.ts | 5 +++-- .../sections/home/snapshot_list/snapshot_list.tsx | 4 ++-- .../app/sections/repository_add/repository_add.tsx | 5 +++-- .../plugins/uptime/public/hooks/use_url_params.ts | 10 +++++----- .../endpoint/store/alerts/middleware.ts | 8 ++++---- 27 files changed, 81 insertions(+), 72 deletions(-) diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 958b8c2636d19..4d9bb89f14b1c 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -21,11 +21,11 @@ import React, { CSSProperties, useCallback, useEffect, useRef, useState } from ' import { EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { debounce } from 'lodash'; +import { parse } from 'query-string'; import { EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useServicesContext, useEditorReadContext } from '../../../../contexts'; import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode'; import { ConsoleMenu } from '../../../../components'; -import { url as urlUtils } from '../../../../../../../../plugins/kibana_utils/public'; import { autoIndent, getDocumentation } from '../console_menu_actions'; import { registerCommands } from './keyboard_shortcuts'; @@ -92,10 +92,10 @@ function EditorUI({ initialTextValue }: EditorProps) { editorInstanceRef.current = senseEditor.create(editorRef.current!); const editor = editorInstanceRef.current; - const readQueryParams = () => { + const readQueryParams = (): Record => { const [, queryString] = (window.location.hash || '').split('?'); - return urlUtils.parseUrlQuery(queryString || ''); + return parse(queryString || '', { sort: false }); }; const loadBufferFromRemote = (url: string) => { diff --git a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts index 77172dc3e425c..2220ad4eef1b7 100644 --- a/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts +++ b/src/plugins/data/public/query/timefilter/lib/parse_querystring.ts @@ -16,8 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - -import { url } from '../../../../../kibana_utils/public'; +import { parse } from 'query-string'; export function parseQueryString() { // window.location.search is an empty string @@ -27,5 +26,5 @@ export function parseQueryString() { return {}; } - return url.parseUrlQuery(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/src/plugins/kibana_utils/common/url/index.ts b/src/plugins/kibana_utils/common/url/index.ts index c7c8f07f00a52..68e14186750da 100644 --- a/src/plugins/kibana_utils/common/url/index.ts +++ b/src/plugins/kibana_utils/common/url/index.ts @@ -18,10 +18,9 @@ */ import { encodeQueryComponent } from './encode_query_component'; -import { stringifyUrlQuery, parseUrlQuery } from './utils'; +import { stringifyUrlQuery } from './utils'; export const url = { encodeQueryComponent, stringifyUrlQuery, - parseUrlQuery, }; diff --git a/src/plugins/kibana_utils/common/url/utils.ts b/src/plugins/kibana_utils/common/url/utils.ts index a4d01fa2454ca..64cff66232411 100644 --- a/src/plugins/kibana_utils/common/url/utils.ts +++ b/src/plugins/kibana_utils/common/url/utils.ts @@ -17,13 +17,9 @@ * under the License. */ import { transform, isUndefined } from 'lodash'; -import { stringify, parse, StringifyOptions } from 'query-string'; +import { stringify, StringifyOptions } from 'query-string'; import { encodeQueryComponent } from './encode_query_component'; -export const parseUrlQuery = >(val: string): TReturn => { - return (parse(val, { sort: false }) as unknown) as TReturn; -}; - export const stringifyUrlQuery = ( query: Record, encodeFunction: diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index fe46ce4168538..4b30d210355ca 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -17,13 +17,14 @@ * under the License. */ +import { parse } from 'query-string'; import { History, Location } from 'history'; import { url } from '../../common'; export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; const search = (oldLocation.search || '').replace(/^\?/, ''); - const query = url.parseUrlQuery(search); + const query = parse(search, { sort: false }); delete query[param]; diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/timelion/server/series_functions/quandl.test.js index 1722a5404e2dc..67d81e56f145f 100644 --- a/src/plugins/timelion/server/series_functions/quandl.test.js +++ b/src/plugins/timelion/server/series_functions/quandl.test.js @@ -17,8 +17,8 @@ * under the License. */ +import { parse } from 'query-string'; import fn from './quandl'; -import { url as urlUtils } from '../../../kibana_utils/server'; import moment from 'moment'; import fetchMock from 'node-fetch'; @@ -26,7 +26,7 @@ const parseURL = require('url').parse; const tlConfig = require('./fixtures/tl_config')(); function parseUrlParams(url) { - return urlUtils.parseUrlQuery(parseURL(url).query); + return parse(parseURL(url).query, { sort: false }); } jest.mock('node-fetch', () => diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index 08233c3503018..2e59f01aad11b 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -3,11 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + +import { parse } from 'query-string'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; export function toQuery(search?: string): APMQueryParamsRaw { - return search ? url.parseUrlQuery(search.slice(1)) : {}; + return search ? parse(search.slice(1), { sort: false }) : {}; } export function fromQuery(query: Record) { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx index 4056e94b6d529..27618dadcabc4 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx +++ b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { FlatObject } from '../frontend_types'; @@ -31,9 +32,9 @@ export class WithURLStateComponent extends React.Compon > { private get URLState(): URLState { // slice because parse does not account for the initial ? in the search string - return url.parseUrlQuery( - decodeURIComponent(this.props.history.location.search).substring(1) - ); + return parse(decodeURIComponent(this.props.history.location.search).substring(1), { + sort: false, + }) as URLState; } private historyListener: (() => void) | null = null; diff --git a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts index 1f03509f84151..d431202ba75a4 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/app_state.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/app_state.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { get } from 'lodash'; // @ts-ignore untyped local import { getInitialState } from '../state/initial_state'; @@ -14,7 +15,6 @@ import { historyProvider } from './history_provider'; import { routerProvider } from './router_provider'; import { createTimeInterval, isValidTimeInterval, getTimeInterval } from './time_interval'; import { AppState, AppStateKeys } from '../../types'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; export function getDefaultAppState(): AppState { const transientState = getInitialState('transient'); @@ -38,7 +38,7 @@ export function getDefaultAppState(): AppState { export function getCurrentAppState(): AppState { const history = historyProvider(getWindow()); const { search } = history.getLocation(); - const qs = !!search ? url.parseUrlQuery(search.replace(/^\?/, '')) : {}; + const qs = !!search ? parse(search.replace(/^\?/, ''), { sort: false }) : {}; const appState = assignAppState({}, qs); return appState; diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js index 0949583f0c4db..af462bfeffcf5 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; +import { parse } from 'query-string'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return url.parseUrlQuery(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx index 633ea62994c92..38cbfb81e312c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { Location } from 'history'; import omit from 'lodash/fp/omit'; import React from 'react'; @@ -102,7 +103,7 @@ const encodeRisonAppState = (state: AnyObject) => ({ export const mapRisonAppLocationToState = ( mapState: (risonAppState: AnyObject) => State = (state: AnyObject) => state as State ) => (location: Location): State => { - const queryValues = url.parseUrlQuery(location.search.substring(1)); + const queryValues = parse(location.search.substring(1), { sort: false }); const decodedState = decodeRisonAppState(queryValues); return mapState(decodedState); }; @@ -110,7 +111,7 @@ export const mapRisonAppLocationToState = ( export const mapStateToRisonAppLocation = ( mapState: (state: State) => AnyObject = (state: State) => state ) => (state: State, location: Location): Location => { - const previousQueryValues = url.parseUrlQuery(location.search.substring(1)); + const previousQueryValues = parse(location.search.substring(1), { sort: false }); const previousState = decodeRisonAppState(previousQueryValues); const encodedState = encodeRisonAppState({ diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index 6307a43519e65..47edde6d72fbf 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { History, Location } from 'history'; import throttle from 'lodash/fp/throttle'; import React from 'react'; @@ -144,7 +145,7 @@ const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (location: Location) => location.search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString = url.parseUrlQuery(queryString); + const parsedQueryString: Record = parse(queryString, { sort: false }); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -154,7 +155,7 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = url.parseUrlQuery(queryString); + const previousQueryValues = parse(queryString, { sort: false }); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index b23375bcdafb4..4c70e05da7caa 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, encode, RisonValue } from 'rison-node'; @@ -100,7 +101,7 @@ const encodeRisonUrlState = (state: any) => encode(state); const getQueryStringFromLocation = (location: Location) => location.search.substring(1); const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = url.parseUrlQuery(queryString); + const parsedQueryString: Record = parse(queryString, { sort: false }); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -110,7 +111,7 @@ export const replaceStateKeyInQueryString = ( stateKey: string, urlState: UrlState | undefined ) => (queryString: string) => { - const previousQueryValues = url.parseUrlQuery(queryString); + const previousQueryValues = parse(queryString, { sort: false }); const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index 311d6ce509249..0da2840c9c2da 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { decode } from 'rison-node'; @@ -14,7 +15,6 @@ import { Page } from '../../../data_frame_analytics/pages/analytics_exploration' import { ANALYSIS_CONFIG_TYPE } from '../../../data_frame_analytics/common/analytics'; import { DATA_FRAME_TASK_STATE } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; import { ML_BREADCRUMB } from '../../breadcrumbs'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -34,7 +34,7 @@ export const analyticsJobExplorationRoute: MlRoute = { const PageWrapper: FC = ({ location, config, deps }) => { const { context } = useResolver('', undefined, config, basicResolvers(deps)); - const { _g }: Record = url.parseUrlQuery(location.search); + const { _g }: Record = parse(location.search, { sort: false }); let globalState: any = null; try { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx index 7f0f0f3e8e8c7..e801a6bc54b59 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -15,7 +16,6 @@ import { checkGetJobsPrivilege } from '../../../privilege/check_privilege'; import { loadIndexPatterns } from '../../../util/index_utils'; import { checkMlNodesAvailable } from '../../../ml_nodes_check'; import { DATA_VISUALIZER_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -35,7 +35,7 @@ export const indexBasedRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context } = useResolver(index, savedSearchId, config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx index 4e70749db8b50..84a6471b02790 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/job_type.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -11,7 +12,7 @@ import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; import { Page } from '../../../jobs/new_job/pages/job_type'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; + const breadcrumbs = [ ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, @@ -30,7 +31,7 @@ export const jobTypeRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context } = useResolver(index, savedSearchId, config, basicResolvers(deps)); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx index e967983cfd0b7..4f1d48a9e657e 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/recognize.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; @@ -13,7 +14,6 @@ import { Page } from '../../../jobs/new_job/recognize'; import { checkViewOrCreateJobs } from '../../../jobs/new_job/recognize/resolvers'; import { mlJobService } from '../../../services/job_service'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; const breadcrumbs = [ ML_BREADCRUMB, @@ -41,7 +41,7 @@ export const checkViewOrCreateRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, deps }) => { - const { id, index, savedSearchId } = url.parseUrlQuery(location.search); + const { id, index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), existingJobsAndGroups: mlJobService.getJobAndGroupIds, @@ -55,7 +55,9 @@ const PageWrapper: FC = ({ location, config, deps }) => { }; const CheckViewOrCreateWrapper: FC = ({ location, config, deps }) => { - const { id: moduleId, index: indexPatternId } = url.parseUrlQuery(location.search); + const { id: moduleId, index: indexPatternId }: Record = parse(location.search, { + sort: false, + }); // the single resolver checkViewOrCreateJobs redirects only. so will always reject useResolver(undefined, undefined, config, { diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index c60a8dfbcb828..f9223dee97394 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; @@ -16,7 +17,6 @@ import { mlJobService } from '../../../services/job_service'; import { loadNewJobCapabilities } from '../../../services/new_job_capabilities_service'; import { checkCreateJobsPrivilege } from '../../../privilege/check_privilege'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; interface WizardPageProps extends PageProps { jobType: JOB_TYPE; @@ -122,7 +122,7 @@ export const categorizationRoute: MlRoute = { }; const PageWrapper: FC = ({ location, config, jobType, deps }) => { - const { index, savedSearchId }: Record = url.parseUrlQuery(location.search); + const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context, results } = useResolver(index, savedSearchId, config, { ...basicResolvers(deps), privileges: checkCreateJobsPrivilege, diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 63dd92a96cc1f..13ba251eac3d5 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { useCallback } from 'react'; import { isEqual } from 'lodash'; import { decode, encode } from 'rison-node'; @@ -32,7 +33,7 @@ function isRisonSerializationRequired(queryParam: string): boolean { export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; - const parsedQueryString: Record = url.parseUrlQuery(search); + const parsedQueryString: Record = parse(search, { sort: false }); try { Object.keys(parsedQueryString).forEach(a => { @@ -63,7 +64,7 @@ export const useUrlState = (accessor: string): UrlState => { const setUrlState = useCallback( (attribute: string | Dictionary, value?: any) => { const urlState = getUrlState(search); - const parsedQueryString = url.parseUrlQuery(search); + const parsedQueryString = parse(search, { sort: false }); if (!Object.prototype.hasOwnProperty.call(urlState, accessor)) { urlState[accessor] = {}; diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js index 0949583f0c4db..af462bfeffcf5 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js +++ b/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; +import { parse } from 'query-string'; export function extractQueryParams(queryString) { const hrefSplit = queryString.split('?'); @@ -12,5 +12,5 @@ export function extractQueryParams(queryString) { return {}; } - return url.parseUrlQuery(hrefSplit[1]); + return parse(hrefSplit[1], { sort: false }); } diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index f87748909add6..b67122a1f17f1 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; @@ -29,9 +30,9 @@ export const MlHostConditionalContainer = React.memo(({ exact path={url} render={({ location }) => { - const queryStringDecoded = urlUtils.parseUrlQuery( - location.search.substring(1) - ); + const queryStringDecoded = parse(location.search.substring(1), { + sort: false, + }) as Required; if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); @@ -48,9 +49,9 @@ export const MlHostConditionalContainer = React.memo(({ params: { hostName }, }, }) => { - const queryStringDecoded = urlUtils.parseUrlQuery( - location.search.substring(1) - ); + const queryStringDecoded = parse(location.search.substring(1), { + sort: false, + }) as Required; if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx index 89c5de78abda8..db6456c4f47df 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; @@ -28,9 +29,9 @@ export const MlNetworkConditionalContainer = React.memo { - const queryStringDecoded = urlUtils.parseUrlQuery( - location.search.substring(1) - ); + const queryStringDecoded = parse(location.search.substring(1), { + sort: false, + }) as Required; if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); @@ -49,9 +50,9 @@ export const MlNetworkConditionalContainer = React.memo { - const queryStringDecoded = urlUtils.parseUrlQuery( - location.search.substring(1) - ); + const queryStringDecoded = parse(location.search.substring(1), { + sort: false, + }) as Required; if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index e4b8ff7ed9988..9e56a2cb5617d 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { decode, encode } from 'rison-node'; import * as H from 'history'; import { Query, esFilters } from 'src/plugins/data/public'; @@ -41,7 +42,7 @@ export const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (search: string) => search.substring(1); export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString = url.parseUrlQuery(queryString); + const parsedQueryString: Record = parse(queryString, { sort: false }); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; @@ -50,7 +51,7 @@ export const getParamFromQueryString = (queryString: string, key: string): strin export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) => ( queryString: string ): string => { - const previousQueryValues = url.parseUrlQuery(queryString); + const previousQueryValues = parse(queryString, { sort: false }); if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx index 5837b8746c9ff..8192fe4e026af 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_list.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import React, { Fragment, useState, useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { EuiButton, EuiCallOut, EuiLink, EuiEmptyPrompt, EuiSpacer, EuiIcon } from '@elastic/eui'; @@ -23,7 +24,6 @@ import { linkToSnapshot, } from '../../../services/navigation'; import { uiMetricService } from '../../../services/ui_metric'; -import { url } from '../../../../../../../../../src/plugins/kibana_utils/public'; import { SnapshotDetails } from './snapshot_details'; import { SnapshotTable } from './snapshot_table'; @@ -86,7 +86,7 @@ export const SnapshotList: React.FunctionComponent(undefined); useEffect(() => { if (search) { - const parsedParams = url.parseUrlQuery(search.replace(/^\?/, '')); + const parsedParams = parse(search.replace(/^\?/, ''), { sort: false }); const { repository, policy } = parsedParams; if (policy && policy !== filteredPolicy) { diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx index 15f2625944f3d..a12ecb4baef5d 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/repository_add/repository_add.tsx @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + +import { parse } from 'query-string'; import React, { useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; @@ -14,7 +16,6 @@ import { BASE_PATH, Section } from '../../constants'; import { useAppDependencies } from '../../index'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { addRepository } from '../../services/http'; -import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; export const RepositoryAdd: React.FunctionComponent = ({ history, @@ -44,7 +45,7 @@ export const RepositoryAdd: React.FunctionComponent = ({ if (error) { setSaveError(error); } else { - const { redirect } = url.parseUrlQuery(search.replace(/^\?/, '')); + const { redirect } = parse(search.replace(/^\?/, ''), { sort: false }); history.push(redirect ? (redirect as string) : `${BASE_PATH}/${section}/${name}`); } diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index be0173644f21a..141216d8f4a33 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { UptimeUrlParams, getSupportedUrlParams } from '../lib/helper'; import { url } from '../../../../../../src/plugins/kibana_utils/public'; @@ -23,18 +24,17 @@ export const useUrlParams: UptimeUrlParamsHook = () => { search = location.search; } - const params = search - ? { - ...url.parseUrlQuery(search[0] === '?' ? search.slice(1) : search), - } + const params: Record = search + ? parse(search[0] === '?' ? search.slice(1) : search, { sort: false }) : {}; + return getSupportedUrlParams(params); }; const updateUrlParams: UpdateUrlParams = updatedParams => { if (!history || !location) return; const { pathname, search } = location; - const currentParams = url.parseUrlQuery(search[0] === '?' ? search.slice(1) : search); + const currentParams = parse(search[0] === '?' ? search.slice(1) : search, { sort: false }); const mergedParams = { ...currentParams, ...updatedParams, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index 5eb856c7f9ead..4a7fac147852b 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -4,18 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +import { parse } from 'query-string'; import { HttpFetchQuery } from 'src/core/public'; import { AppAction } from '../action'; -import { MiddlewareFactory } from '../../types'; -import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { MiddlewareFactory, AlertListData } from '../../types'; export const alertMiddlewareFactory: MiddlewareFactory = coreStart => { - const qp = url.parseUrlQuery(window.location.search.slice(1)); + const qp = parse(window.location.search.slice(1), { sort: false }); return api => next => async (action: AppAction) => { next(action); if (action.type === 'userNavigatedToPage' && action.payload === 'alertsPage') { - const response = await coreStart.http.get('/api/endpoint/alerts', { + const response: AlertListData = await coreStart.http.get('/api/endpoint/alerts', { query: qp as HttpFetchQuery, }); api.dispatch({ type: 'serverReturnedAlertsData', payload: response }); From 46f6a4837c9d34f2cba6c478ada65cb8a6698d87 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 11 Feb 2020 14:07:43 +0300 Subject: [PATCH 14/18] remove url.stringifyUrlQuery --- src/legacy/server/logging/log_format.js | 4 +- src/plugins/console/public/lib/es/es.ts | 4 +- .../common/url/encode_query_component.test.ts | 48 ------------- .../common/url/encode_uri_query.test.ts | 70 +++++++++++++++++++ ...query_component.ts => encode_uri_query.ts} | 14 +++- src/plugins/kibana_utils/common/url/index.ts | 7 +- .../kibana_utils/common/url/utils.test.ts | 37 ---------- src/plugins/kibana_utils/common/url/utils.ts | 44 ------------ .../public/history/remove_query_param.ts | 5 +- .../public/state_management/url/format.ts | 4 +- .../state_management/url/kbn_url_storage.ts | 6 +- .../components/shared/Links/url_helpers.ts | 6 +- .../public/containers/with_url_state.tsx | 14 ++-- .../workpad_header/workpad_export/utils.ts | 3 +- .../public/app/services/routing.js | 4 +- .../analyze_in_ml_button.tsx | 6 +- .../containers/with_state_from_location.tsx | 14 ++-- .../plugins/infra/public/utils/url_state.tsx | 14 ++-- .../infra/public/utils/use_url_state.ts | 14 ++-- .../ml/public/application/util/url_state.ts | 9 ++- .../printable_pdf/server/lib/uri_encode.js | 10 +-- .../reporting/public/lib/reporting_client.ts | 5 +- .../ml_host_conditional_container.tsx | 11 ++- .../ml_network_conditional_container.tsx | 11 ++- .../public/components/url_state/helpers.ts | 18 ++--- .../uptime/public/hooks/use_url_params.ts | 8 +-- .../public/lib/helper/stringify_url_params.ts | 4 +- .../plugins/uptime/public/state/api/ping.ts | 4 +- 28 files changed, 177 insertions(+), 221 deletions(-) delete mode 100644 src/plugins/kibana_utils/common/url/encode_query_component.test.ts create mode 100644 src/plugins/kibana_utils/common/url/encode_uri_query.test.ts rename src/plugins/kibana_utils/common/url/{encode_query_component.ts => encode_uri_query.ts} (79%) delete mode 100644 src/plugins/kibana_utils/common/url/utils.test.ts delete mode 100644 src/plugins/kibana_utils/common/url/utils.ts diff --git a/src/legacy/server/logging/log_format.js b/src/legacy/server/logging/log_format.js index b3b07ce7802ee..ca1d756704dd0 100644 --- a/src/legacy/server/logging/log_format.js +++ b/src/legacy/server/logging/log_format.js @@ -20,13 +20,13 @@ import Stream from 'stream'; import moment from 'moment-timezone'; import { get, _ } from 'lodash'; +import queryString from 'query-string'; import numeral from '@elastic/numeral'; import chalk from 'chalk'; import stringify from 'json-stringify-safe'; import applyFiltersToKeys from './apply_filters_to_keys'; import { inspect } from 'util'; import { logWithMetadata } from './log_with_metadata'; -import { url } from '../../../plugins/kibana_utils/server'; function serializeError(err = {}) { return { @@ -108,7 +108,7 @@ export default class TransformObjStream extends Stream.Transform { contentLength: contentLength, }; - const query = url.stringifyUrlQuery(event.query); + const query = queryString.stringify(event.query, { sort: false }); if (query) data.req.url += '?' + query; data.message = data.req.method.toUpperCase() + ' '; diff --git a/src/plugins/console/public/lib/es/es.ts b/src/plugins/console/public/lib/es/es.ts index c3f08d0e385a0..f11692e1befad 100644 --- a/src/plugins/console/public/lib/es/es.ts +++ b/src/plugins/console/public/lib/es/es.ts @@ -18,7 +18,7 @@ */ import $ from 'jquery'; -import { url } from '../../../../kibana_utils/public'; +import { stringify } from 'query-string'; const esVersion: string[] = []; @@ -35,7 +35,7 @@ export function send(method: string, path: string, data: any) { const wrappedDfd = $.Deferred(); // eslint-disable-line new-cap const options: JQuery.AjaxSettings = { - url: '../api/console/proxy?' + url.stringifyUrlQuery({ path, method }), + url: '../api/console/proxy?' + stringify({ path, method }, { sort: false }), data, contentType: getContentType(data), cache: false, diff --git a/src/plugins/kibana_utils/common/url/encode_query_component.test.ts b/src/plugins/kibana_utils/common/url/encode_query_component.test.ts deleted file mode 100644 index 228c711e23ad0..0000000000000 --- a/src/plugins/kibana_utils/common/url/encode_query_component.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you 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 { encodeQueryComponent } from './encode_query_component'; - -describe('encodeUriQuery', function() { - it('should correctly encode uri query and not encode chars defined as pchar set in rfc3986', () => { - // don't encode alphanum - expect(encodeQueryComponent('asdf1234asdf')).toBe('asdf1234asdf'); - - // don't encode unreserved - expect(encodeQueryComponent("-_.!~*'() -_.!~*'()")).toBe("-_.!~*'()+-_.!~*'()"); - - // don't encode the rest of pchar - expect(encodeQueryComponent(':@$, :@$,')).toBe(':@$,+:@$,'); - - // encode '&', ';', '=', '+', and '#' - expect(encodeQueryComponent('&;=+# &;=+#')).toBe('%26;%3D%2B%23+%26;%3D%2B%23'); - - // encode ' ' as '+' - expect(encodeQueryComponent(' ')).toBe('++'); - - // encode ' ' as '%20' when a flag is used - expect(encodeQueryComponent(' ', true)).toBe('%20%20'); - - // do not encode `null` as '+' when flag is used - expect(encodeQueryComponent('null', true)).toBe('null'); - - // do not encode `null` with no flag - expect(encodeQueryComponent('null')).toBe('null'); - }); -}); diff --git a/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts b/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts new file mode 100644 index 0000000000000..b600822946299 --- /dev/null +++ b/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { encodeUriQuery, encodeQuery } from './encode_uri_query'; + +describe('encodeUriQuery', () => { + test('should correctly encode uri query and not encode chars defined as pchar set in rfc3986', () => { + // don't encode alphanum + expect(encodeUriQuery('asdf1234asdf')).toBe('asdf1234asdf'); + + // don't encode unreserved + expect(encodeUriQuery("-_.!~*'() -_.!~*'()")).toBe("-_.!~*'()+-_.!~*'()"); + + // don't encode the rest of pchar + expect(encodeUriQuery(':@$, :@$,')).toBe(':@$,+:@$,'); + + // encode '&', ';', '=', '+', and '#' + expect(encodeUriQuery('&;=+# &;=+#')).toBe('%26;%3D%2B%23+%26;%3D%2B%23'); + + // encode ' ' as '+' + expect(encodeUriQuery(' ')).toBe('++'); + + // encode ' ' as '%20' when a flag is used + expect(encodeUriQuery(' ', true)).toBe('%20%20'); + + // do not encode `null` as '+' when flag is used + expect(encodeUriQuery('null', true)).toBe('null'); + + // do not encode `null` with no flag + expect(encodeUriQuery('null')).toBe('null'); + }); +}); + +describe('encodeQuery', () => { + test('encodeQuery', () => { + expect( + encodeQuery({ + a: 'asdf1234asdf', + b: "-_.!~*'() -_.!~*'()", + c: ':@$, :@$,', + d: "&;=+# &;=+#'", + f: ' ', + g: 'null', + }) + ).toEqual({ + a: 'asdf1234asdf', + b: "-_.!~*'()%20-_.!~*'()", + c: ':@$,%20:@$,', + d: "%26;%3D%2B%23%20%26;%3D%2B%23'", + f: '%20', + g: 'null', + }); + }); +}); diff --git a/src/plugins/kibana_utils/common/url/encode_query_component.ts b/src/plugins/kibana_utils/common/url/encode_uri_query.ts similarity index 79% rename from src/plugins/kibana_utils/common/url/encode_query_component.ts rename to src/plugins/kibana_utils/common/url/encode_uri_query.ts index 5b54575653418..e65a8c2cc1581 100644 --- a/src/plugins/kibana_utils/common/url/encode_query_component.ts +++ b/src/plugins/kibana_utils/common/url/encode_uri_query.ts @@ -17,6 +17,8 @@ * under the License. */ +import { isUndefined, transform } from 'lodash'; + /** * This method is intended for encoding *key* or *value* parts of query component. We need a custom * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be @@ -28,7 +30,7 @@ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" */ -export function encodeQueryComponent(val: string, pctEncodeSpaces = false) { +export function encodeUriQuery(val: string, pctEncodeSpaces = false) { return encodeURIComponent(val) .replace(/%40/gi, '@') .replace(/%3A/gi, ':') @@ -37,3 +39,13 @@ export function encodeQueryComponent(val: string, pctEncodeSpaces = false) { .replace(/%3B/gi, ';') .replace(/%20/g, pctEncodeSpaces ? '%20' : '+'); } + +export const encodeQuery = ( + query: Record, + encodeFunction: (val: string, pctEncodeSpaces?: boolean) => string = encodeUriQuery +) => + transform(query, (result, value, key) => { + if (key) { + result[key] = encodeFunction(isUndefined(value) ? '' : value, true); + } + }); diff --git a/src/plugins/kibana_utils/common/url/index.ts b/src/plugins/kibana_utils/common/url/index.ts index 68e14186750da..7b74f07e598ee 100644 --- a/src/plugins/kibana_utils/common/url/index.ts +++ b/src/plugins/kibana_utils/common/url/index.ts @@ -17,10 +17,9 @@ * under the License. */ -import { encodeQueryComponent } from './encode_query_component'; -import { stringifyUrlQuery } from './utils'; +import { encodeUriQuery, encodeQuery } from './encode_uri_query'; export const url = { - encodeQueryComponent, - stringifyUrlQuery, + encodeQuery, + encodeUriQuery, }; diff --git a/src/plugins/kibana_utils/common/url/utils.test.ts b/src/plugins/kibana_utils/common/url/utils.test.ts deleted file mode 100644 index 6bbf6416e12dc..0000000000000 --- a/src/plugins/kibana_utils/common/url/utils.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you 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 { stringifyUrlQuery } from './utils'; - -describe('stringifyUrlQuery', () => { - it('stringifyUrlQuery', () => { - expect( - stringifyUrlQuery({ - a: 'asdf1234asdf', - b: "-_.!~*'() -_.!~*'()", - c: ':@$, :@$,', - d: "&;=+# &;=+#'", - f: ' ', - g: 'null', - }) - ).toBe( - `a=asdf1234asdf&b=-_.!~*'()%20-_.!~*'()&c=:@$,%20:@$,&d=%26;%3D%2B%23%20%26;%3D%2B%23'&f=%20&g=null` - ); - }); -}); diff --git a/src/plugins/kibana_utils/common/url/utils.ts b/src/plugins/kibana_utils/common/url/utils.ts deleted file mode 100644 index 64cff66232411..0000000000000 --- a/src/plugins/kibana_utils/common/url/utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you 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 { transform, isUndefined } from 'lodash'; -import { stringify, StringifyOptions } from 'query-string'; -import { encodeQueryComponent } from './encode_query_component'; - -export const stringifyUrlQuery = ( - query: Record, - encodeFunction: - | false - | ((val: string, pctEncodeSpaces?: boolean) => string) = encodeQueryComponent, - options: StringifyOptions = {} -) => { - const encodedQuery = - encodeFunction && - transform(query, (result, value, key) => { - if (key) { - result[key] = encodeFunction(isUndefined(value) ? '' : value, true); - } - }); - - return stringify(encodedQuery || query, { - strict: false, - encode: false, - sort: false, - ...options, - }); -}; diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index 4b30d210355ca..09e9ff9ed841f 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -17,9 +17,8 @@ * under the License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { History, Location } from 'history'; -import { url } from '../../common'; export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; @@ -28,7 +27,7 @@ export function removeQueryParam(history: History, param: string, replace: boole delete query[param]; - const newSearch = url.stringifyUrlQuery(query); + const newSearch = stringify(query, { sort: false }); const newLocation: Location = { ...oldLocation, search: newSearch, diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 5fdd035f2ff69..79be1acd26bdd 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -18,8 +18,8 @@ */ import { format as formatUrl } from 'url'; +import { stringify } from 'query-string'; import { parseUrl, parseUrlHash } from './parse'; -import { url as urlUtils } from '../../../common'; export function replaceUrlHashQuery( rawUrl: string, @@ -28,7 +28,7 @@ export function replaceUrlHashQuery( const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); const newQuery = queryReplacer(hash?.query || {}); - const searchQueryString = urlUtils.stringifyUrlQuery(newQuery); + const searchQueryString = stringify(newQuery, { sort: false }); if ((!hash || !hash.search) && !searchQueryString) return rawUrl; // nothing to change. return original url return formatUrl({ diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index d4f4ef90b3e23..84094c4fac6b3 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -18,11 +18,11 @@ */ import { format as formatUrl } from 'url'; +import { stringify } from 'query-string'; import { createBrowserHistory, History } from 'history'; import { decodeState, encodeState } from '../state_encoder'; import { getCurrentUrl, parseUrl, parseUrlHash } from './parse'; import { replaceUrlHashQuery } from './format'; -import { url as urlUtils } from '../../../common'; /** * Parses a kibana url and retrieves all the states encoded into url, @@ -243,11 +243,11 @@ export function getRelativeToHistoryPath(absoluteUrl: string, history: History): return formatUrl({ pathname: stripBasename(parsedUrl.pathname), - search: urlUtils.stringifyUrlQuery(parsedUrl.query), + search: stringify(parsedUrl.query, { sort: false }), hash: parsedHash ? formatUrl({ pathname: parsedHash.pathname, - search: urlUtils.stringifyUrlQuery(parsedHash.query), + search: stringify(parsedHash.query, { sort: false }), }) : parsedUrl.hash, }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index 2e59f01aad11b..36465309b736e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; @@ -13,9 +13,11 @@ export function toQuery(search?: string): APMQueryParamsRaw { } export function fromQuery(query: Record) { - return url.stringifyUrlQuery(query, (value: string) => + const encodedQuery = url.encodeQuery(query, value => encodeURIComponent(value).replace(/%3A/g, ':') ); + + return stringify(encodedQuery, { sort: false, encode: false }); } export type APMQueryParams = { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx index 27618dadcabc4..c8f756da985a7 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx +++ b/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { FlatObject } from '../frontend_types'; import { RendererFunction } from '../utils/typed_react'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; type StateCallback = (previousState: T) => T; @@ -66,10 +65,13 @@ export class WithURLStateComponent extends React.Compon newState = state; } - const search: string = url.stringifyUrlQuery({ - ...pastState, - ...newState, - }); + const search: string = stringify( + { + ...pastState, + ...newState, + }, + { sort: false } + ); const newLocation = { ...this.props.history.location, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index e14cf70d3b2f7..5adbf4ce66c13 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -72,8 +72,7 @@ function getPdfUrlParts( export function getPdfUrl(...args: Arguments): string { const urlParts = getPdfUrlParts(...args); const param = (key: string, val: any) => - url.encodeQueryComponent(key, true) + - (val === true ? '' : '=' + url.encodeQueryComponent(val, true)); + url.encodeUriQuery(key, true) + (val === true ? '' : '=' + url.encodeUriQuery(val, true)); return `${urlParts.createPdfUri}?${param('jobParams', urlParts.createPdfPayload.jobParams)}`; } diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js index a665891ba8763..487b1068794f9 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/services/routing.js @@ -9,7 +9,7 @@ */ import { createLocation } from 'history'; -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; +import { stringify } from 'query-string'; import { APPS, BASE_PATH, BASE_PATH_REMOTE_CLUSTERS } from '../../../common/constants'; const isModifiedEvent = event => @@ -22,7 +22,7 @@ const queryParamsFromObject = (params, encodeParams = false) => { return; } - const paramsStr = url.stringifyUrlQuery(params, encodeParams ? undefined : false); + const paramsStr = stringify(params, { sort: false, encode: encodeParams }); return `?${paramsStr}`; }; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index 4fbfc488522ee..c9291422a95ac 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -9,8 +9,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { encode } from 'rison-node'; import url from 'url'; +import { stringify } from 'query-string'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; export const AnalyzeInMlButton: React.FunctionComponent<{ @@ -61,7 +61,7 @@ const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRang }, }); - const hash = `/explorer?${urlUtils.stringifyUrlQuery({ _g })}`; + const hash = `/explorer?${stringify({ _g })}`; return url.format({ pathname, @@ -94,7 +94,7 @@ const getPartitionSpecificSingleMetricViewerLink = ( }, }); - const hash = `/timeseriesexplorer?${urlUtils.stringifyUrlQuery({ _g, _a })}`; + const hash = `/timeseriesexplorer?${stringify({ _g, _a }, { sort: false })}`; return url.format({ pathname, diff --git a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx index 38cbfb81e312c..6f7baf6b98b62 100644 --- a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { Location } from 'history'; import omit from 'lodash/fp/omit'; import React from 'react'; @@ -12,7 +12,6 @@ import { RouteComponentProps, withRouter } from 'react-router-dom'; // eslint-disable-next-line @typescript-eslint/camelcase import { decode_object, encode_object } from 'rison-node'; import { Omit } from '../lib/lib'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; interface AnyObject { [key: string]: any; @@ -118,10 +117,13 @@ export const mapStateToRisonAppLocation = ( ...previousState, ...mapState(state), }); - const newQueryValues = url.stringifyUrlQuery({ - ...previousQueryValues, - ...encodedState, - }); + const newQueryValues = stringify( + { + ...previousQueryValues, + ...encodedState, + }, + { sort: false } + ); return { ...location, search: `?${newQueryValues}`, diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index 47edde6d72fbf..c1321b39d6a67 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { History, Location } from 'history'; import throttle from 'lodash/fp/throttle'; import React from 'react'; import { Route, RouteProps } from 'react-router-dom'; import { decode, encode, RisonValue } from 'rison-node'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; interface UrlStateContainerProps { urlState: UrlState | undefined; @@ -159,10 +158,13 @@ export const replaceStateKeyInQueryString = ( const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return url.stringifyUrlQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index 4c70e05da7caa..6c7d1e59b519c 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, encode, RisonValue } from 'rison-node'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; import { useHistory } from './history_context'; @@ -115,10 +114,13 @@ export const replaceStateKeyInQueryString = ( const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return url.stringifyUrlQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 13ba251eac3d5..473e7ebb646b1 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { useCallback } from 'react'; import { isEqual } from 'lodash'; import { decode, encode } from 'rison-node'; import { useHistory, useLocation } from 'react-router-dom'; -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; import { Dictionary } from '../../../common/types/common'; @@ -84,7 +83,7 @@ export const useUrlState = (accessor: string): UrlState => { } try { - const oldLocationSearch = url.stringifyUrlQuery(parsedQueryString, false); + const oldLocationSearch = stringify(parsedQueryString, { sort: false, encode: false }); Object.keys(urlState).forEach(a => { if (isRisonSerializationRequired(a)) { @@ -93,11 +92,11 @@ export const useUrlState = (accessor: string): UrlState => { parsedQueryString[a] = urlState[a]; } }); - const newLocationSearch = url.stringifyUrlQuery(parsedQueryString, false); + const newLocationSearch = stringify(parsedQueryString, { sort: false, encode: false }); if (oldLocationSearch !== newLocationSearch) { history.push({ - search: url.stringifyUrlQuery(parsedQueryString), + search: stringify(parsedQueryString, { sort: false }), }); } } catch (error) { diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js index 005e6c9ff5c1e..f764271c22a2d 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/uri_encode.js @@ -12,13 +12,13 @@ function toKeyValue(obj) { forEach(obj, function(value, key) { if (isArray(value)) { forEach(value, function(arrayValue) { - const keyStr = url.encodeQueryComponent(key, true); - const valStr = arrayValue === true ? '' : '=' + url.encodeQueryComponent(arrayValue, true); + const keyStr = url.encodeUriQuery(key, true); + const valStr = arrayValue === true ? '' : '=' + url.encodeUriQuery(arrayValue, true); parts.push(keyStr + valStr); }); } else { - const keyStr = url.encodeQueryComponent(key, true); - const valStr = value === true ? '' : '=' + url.encodeQueryComponent(value, true); + const keyStr = url.encodeUriQuery(key, true); + const valStr = value === true ? '' : '=' + url.encodeUriQuery(value, true); parts.push(keyStr + valStr); } }); @@ -27,5 +27,5 @@ function toKeyValue(obj) { export const uriEncode = { stringify: toKeyValue, - string: url.encodeQueryComponent, + string: url.encodeUriQuery, }; diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts index 12b8471e758c2..d471dc57fc9e1 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts @@ -3,12 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { stringify } from 'query-string'; import { npStart } from 'ui/new_platform'; // @ts-ignore import rison from 'rison-node'; import { add } from './job_completion_notifications'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; const { core } = npStart; const API_BASE_URL = '/api/reporting/generate'; @@ -18,7 +17,7 @@ interface JobParams { } export const getReportingJobPath = (exportType: string, jobParams: JobParams) => { - const params = url.stringifyUrlQuery({ jobParams: rison.encode(jobParams) }); + const params = stringify({ jobParams: rison.encode(jobParams) }); return `${core.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; }; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index b67122a1f17f1..2f239c216636a 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; @@ -13,7 +13,6 @@ import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; import { SiemPageName } from '../../../pages/home/types'; import { HostsTableType } from '../../../store/hosts/model'; -import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; interface QueryStringType { '?_g': string; @@ -37,7 +36,7 @@ export const MlHostConditionalContainer = React.memo(({ if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; }} /> @@ -57,7 +56,7 @@ export const MlHostConditionalContainer = React.memo(({ queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(hostName)) { - const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ( @@ -69,13 +68,13 @@ export const MlHostConditionalContainer = React.memo(({ hosts, queryStringDecoded.query || '' ); - const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ( ); } else { - const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ( ; }} @@ -59,7 +58,7 @@ export const MlNetworkConditionalContainer = React.memo; } else if (multipleEntities(ip)) { @@ -69,10 +68,10 @@ export const MlNetworkConditionalContainer = React.memo; } else { - const reEncoded = urlUtils.stringifyUrlQuery(queryStringDecoded); + const reEncoded = stringify(queryStringDecoded, { sort: false }); return ; } }} diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 9e56a2cb5617d..128ddb5fc3d2a 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { decode, encode } from 'rison-node'; import * as H from 'history'; import { Query, esFilters } from 'src/plugins/data/public'; @@ -23,7 +23,6 @@ import { Timeline, UpdateUrlStateString, } from './types'; -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; export const decodeRisonUrlState = (value: string | undefined): T | null => { try { @@ -55,9 +54,7 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; - return url.stringifyUrlQuery({ - ...previousQueryValues, - }); + return stringify(previousQueryValues, { sort: false }); } // ಠ_ಠ Code was copied from x-pack/legacy/plugins/infra/public/utils/url_state.tsx ಠ_ಠ @@ -65,10 +62,13 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = const encodedUrlState = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - return url.stringifyUrlQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }); + return stringify( + { + ...previousQueryValues, + [stateKey]: encodedUrlState, + }, + { sort: false } + ); }; export const replaceQueryStringInLocation = ( diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index 141216d8f4a33..9c451db0ed815 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { UptimeUrlParams, getSupportedUrlParams } from '../lib/helper'; -import { url } from '../../../../../../src/plugins/kibana_utils/public'; type GetUrlParams = () => UptimeUrlParams; type UpdateUrlParams = (updatedParams: { [key: string]: string | number | boolean }) => void; @@ -42,7 +41,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { history.push({ pathname, - search: url.stringifyUrlQuery( + search: stringify( // drop any parameters that have no value Object.keys(mergedParams).reduce((params, key) => { const value = mergedParams[key]; @@ -53,7 +52,8 @@ export const useUrlParams: UptimeUrlParamsHook = () => { ...params, [key]: value, }; - }, {}) + }, {}), + { sort: false } ), }); }; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts index fd265cf417d82..a8ce86c4399e2 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { stringify } from 'query-string'; import { UptimeUrlParams } from './url_params'; import { CLIENT_DEFAULTS } from '../../../common/constants'; -import { url } from '../../../../../../../src/plugins/kibana_utils/public'; const { AUTOREFRESH_INTERVAL, @@ -38,5 +38,5 @@ export const stringifyUrlParams = (params: Partial, ignoreEmpty } }); } - return `?${url.stringifyUrlQuery(params)}`; + return `?${stringify(params, { sort: false })}`; }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index 7e7cb0ef804e3..c61bf42c8c90e 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { stringify } from 'query-string'; import { getApiPath } from '../../lib/helper'; import { APIFn } from './types'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; -import { url as urlUtils } from '../../../../../../../src/plugins/kibana_utils/public'; export const fetchPingHistogram: APIFn = async ({ basePath, @@ -25,7 +25,7 @@ export const fetchPingHistogram: APIFn ...(statusFilter && { statusFilter }), ...(filters && { filters }), }; - const urlParams = urlUtils.stringifyUrlQuery(params); + const urlParams = stringify(params, { sort: false }); const response = await fetch(`${url}?${urlParams}`); if (!response.ok) { throw new Error(response.statusText); From 65aaa7840b9d9ae6593c19685e002f7abe751485 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 11 Feb 2020 14:32:56 +0300 Subject: [PATCH 15/18] use url.encodeQuery --- .../public/history/remove_query_param.ts | 3 ++- .../public/state_management/url/format.ts | 6 ++++- .../state_management/url/kbn_url_storage.ts | 5 +++-- .../analyze_in_ml_button.tsx | 8 +++++-- .../plugins/infra/public/utils/url_state.tsx | 7 +++--- .../infra/public/utils/use_url_state.ts | 7 +++--- .../public/application/util/url_state.test.ts | 3 ++- .../ml_host_conditional_container.tsx | 22 +++++++++++++++---- .../ml_network_conditional_container.tsx | 22 +++++++++++++++---- .../public/components/url_state/helpers.ts | 10 +++++---- .../stringify_url_params.test.ts.snap | 4 ++-- 11 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts index 09e9ff9ed841f..bf945e5b064aa 100644 --- a/src/plugins/kibana_utils/public/history/remove_query_param.ts +++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts @@ -19,6 +19,7 @@ import { parse, stringify } from 'query-string'; import { History, Location } from 'history'; +import { url } from '../../common'; export function removeQueryParam(history: History, param: string, replace: boolean = true) { const oldLocation = history.location; @@ -27,7 +28,7 @@ export function removeQueryParam(history: History, param: string, replace: boole delete query[param]; - const newSearch = stringify(query, { sort: false }); + const newSearch = stringify(url.encodeQuery(query), { sort: false, encode: false }); const newLocation: Location = { ...oldLocation, search: newSearch, diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 79be1acd26bdd..18d4cd502f284 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -20,6 +20,7 @@ import { format as formatUrl } from 'url'; import { stringify } from 'query-string'; import { parseUrl, parseUrlHash } from './parse'; +import { url as urlUtils } from '../../../common'; export function replaceUrlHashQuery( rawUrl: string, @@ -28,7 +29,10 @@ export function replaceUrlHashQuery( const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); const newQuery = queryReplacer(hash?.query || {}); - const searchQueryString = stringify(newQuery, { sort: false }); + const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), { + sort: false, + encode: false, + }); if ((!hash || !hash.search) && !searchQueryString) return rawUrl; // nothing to change. return original url return formatUrl({ diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index 84094c4fac6b3..40a411d425a54 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -23,6 +23,7 @@ import { createBrowserHistory, History } from 'history'; import { decodeState, encodeState } from '../state_encoder'; import { getCurrentUrl, parseUrl, parseUrlHash } from './parse'; import { replaceUrlHashQuery } from './format'; +import { url as urlUtils } from '../../../common'; /** * Parses a kibana url and retrieves all the states encoded into url, @@ -243,11 +244,11 @@ export function getRelativeToHistoryPath(absoluteUrl: string, history: History): return formatUrl({ pathname: stripBasename(parsedUrl.pathname), - search: stringify(parsedUrl.query, { sort: false }), + search: stringify(urlUtils.encodeQuery(parsedUrl.query), { sort: false, encode: false }), hash: parsedHash ? formatUrl({ pathname: parsedHash.pathname, - search: stringify(parsedHash.query, { sort: false }), + search: stringify(urlUtils.encodeQuery(parsedHash.query), { sort: false, encode: false }), }) : parsedUrl.hash, }); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx index c9291422a95ac..9fbba94407dc0 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx @@ -12,6 +12,7 @@ import url from 'url'; import { stringify } from 'query-string'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; +import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; export const AnalyzeInMlButton: React.FunctionComponent<{ jobId: string; @@ -61,7 +62,7 @@ const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRang }, }); - const hash = `/explorer?${stringify({ _g })}`; + const hash = `/explorer?${stringify(urlUtils.encodeQuery({ _g }), { encode: false })}`; return url.format({ pathname, @@ -94,7 +95,10 @@ const getPartitionSpecificSingleMetricViewerLink = ( }, }); - const hash = `/timeseriesexplorer?${stringify({ _g, _a }, { sort: false })}`; + const hash = `/timeseriesexplorer?${stringify(urlUtils.encodeQuery({ _g, _a }), { + sort: false, + encode: false, + })}`; return url.format({ pathname, diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx index c1321b39d6a67..58835715fe55c 100644 --- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/url_state.tsx @@ -10,6 +10,7 @@ import throttle from 'lodash/fp/throttle'; import React from 'react'; import { Route, RouteProps } from 'react-router-dom'; import { decode, encode, RisonValue } from 'rison-node'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; interface UrlStateContainerProps { urlState: UrlState | undefined; @@ -159,11 +160,11 @@ export const replaceStateKeyInQueryString = ( typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; return stringify( - { + url.encodeQuery({ ...previousQueryValues, [stateKey]: encodedUrlState, - }, - { sort: false } + }), + { sort: false, encode: false } ); }; diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index 6c7d1e59b519c..ad1dbd676e05b 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -8,6 +8,7 @@ import { parse, stringify } from 'query-string'; import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, encode, RisonValue } from 'rison-node'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; import { useHistory } from './history_context'; @@ -115,11 +116,11 @@ export const replaceStateKeyInQueryString = ( typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; return stringify( - { + url.encodeQuery({ ...previousQueryValues, [stateKey]: encodedUrlState, - }, - { sort: false } + }), + { sort: false, encode: false } ); }; diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts index 44fe4588fc7de..0813f2e3da97f 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts @@ -74,7 +74,8 @@ describe('useUrlState', () => { }); expect(mockHistoryPush).toHaveBeenCalledWith({ - search: `_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:())&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d`, + search: + '_a=%28mlExplorerFilter%3A%28%29%2CmlExplorerSwimlane%3A%28viewByFieldName%3Aaction%29%2Cquery%3A%28%29%29&_g=%28ml%3A%28jobIds%3A%21%28dec-2%29%29%2CrefreshInterval%3A%28display%3AOff%2Cpause%3A%21f%2Cvalue%3A0%29%2Ctime%3A%28from%3A%272019-01-01T00%3A03%3A40.000Z%27%2Cmode%3Aabsolute%2Cto%3A%272019-08-30T11%3A55%3A07.000Z%27%29%29&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d', }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index 2f239c216636a..b5aacdf664c67 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -14,6 +14,8 @@ import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_hel import { SiemPageName } from '../../../pages/home/types'; import { HostsTableType } from '../../../store/hosts/model'; +import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; + interface QueryStringType { '?_g': string; query: string | null; @@ -36,7 +38,10 @@ export const MlHostConditionalContainer = React.memo(({ if (queryStringDecoded.query != null) { queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { + sort: false, + encode: false, + }); return ; }} /> @@ -56,7 +61,10 @@ export const MlHostConditionalContainer = React.memo(({ queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(hostName)) { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { + sort: false, + encode: false, + }); return ( @@ -68,13 +76,19 @@ export const MlHostConditionalContainer = React.memo(({ hosts, queryStringDecoded.query || '' ); - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { + sort: false, + encode: false, + }); return ( ); } else { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { + sort: false, + encode: false, + }); return ( ; }} @@ -58,7 +63,10 @@ export const MlNetworkConditionalContainer = React.memo; } else if (multipleEntities(ip)) { @@ -68,10 +76,16 @@ export const MlNetworkConditionalContainer = React.memo; } else { - const reEncoded = stringify(queryStringDecoded, { sort: false }); + const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { + sort: false, + encode: false, + }); return ; } }} diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 128ddb5fc3d2a..2805d8edf94f5 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -24,6 +24,8 @@ import { UpdateUrlStateString, } from './types'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; + export const decodeRisonUrlState = (value: string | undefined): T | null => { try { return value ? ((decode(value) as unknown) as T) : null; @@ -54,7 +56,7 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; - return stringify(previousQueryValues, { sort: false }); + return stringify(url.encodeQuery(previousQueryValues), { sort: false, encode: false }); } // ಠ_ಠ Code was copied from x-pack/legacy/plugins/infra/public/utils/url_state.tsx ಠ_ಠ @@ -63,11 +65,11 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; return stringify( - { + url.encodeQuery({ ...previousQueryValues, [stateKey]: encodedUrlState, - }, - { sort: false } + }), + { sort: false, encode: false } ); }; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap index 3519937a49956..31f5ceff7d046 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`stringifyUrlParams creates expected string value 1`] = `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id:%20bar&search=monitor.id:%20foo&selectedPingStatus=down&statusFilter=up"`; +exports[`stringifyUrlParams creates expected string value 1`] = `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id%3A%20bar&search=monitor.id%3A%20foo&selectedPingStatus=down&statusFilter=up"`; -exports[`stringifyUrlParams creates expected string value when ignore empty is true 1`] = `"?autorefreshInterval=50000&filters=monitor.id:%20bar"`; +exports[`stringifyUrlParams creates expected string value when ignore empty is true 1`] = `"?autorefreshInterval=50000&filters=monitor.id%3A%20bar"`; From b2723570aaac60a228464f2b51d8c94bc2f14820 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 11 Feb 2020 17:57:07 +0300 Subject: [PATCH 16/18] Record -> ParsedQuery --- src/core/utils/url.ts | 4 ++-- .../editor/legacy/console_editor/editor.tsx | 10 +++++++--- .../kibana_utils/common/url/encode_uri_query.ts | 12 +++++++++--- .../public/state_management/url/format.ts | 4 ++-- .../legacy/plugins/canvas/public/lib/modify_url.ts | 4 +++- .../plugins/infra/public/utils/use_url_state.ts | 6 +++--- .../plugins/ml/public/application/util/url_state.ts | 2 +- .../siem/public/components/url_state/helpers.ts | 4 ++-- .../plugins/uptime/public/hooks/use_url_params.ts | 2 +- .../helper/url_params/get_supported_url_params.ts | 2 +- x-pack/plugins/spaces/server/lib/utils/url.ts | 3 ++- 11 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index 649ad0abee332..31de7e1814038 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -32,7 +32,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: Record; + query: ParsedQuery; } /** diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 4d9bb89f14b1c..65fb4c9fe19e3 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -47,6 +47,10 @@ export interface EditorProps { initialTextValue: string; } +interface QueryParams { + load_from: string; +} + const abs: CSSProperties = { position: 'absolute', top: '0', @@ -92,17 +96,17 @@ function EditorUI({ initialTextValue }: EditorProps) { editorInstanceRef.current = senseEditor.create(editorRef.current!); const editor = editorInstanceRef.current; - const readQueryParams = (): Record => { + const readQueryParams = () => { const [, queryString] = (window.location.hash || '').split('?'); - return parse(queryString || '', { sort: false }); + return parse(queryString || '', { sort: false }) as Required; }; const loadBufferFromRemote = (url: string) => { if (/^https?:\/\//.test(url)) { const loadFrom: Record = { url, - // Having dataType here is required as it doesn't allow jQuery to `eval` content + // Having dataType here i\s required as it doesn't allow jQuery to `eval` content // coming from the external source thereby preventing XSS attack. dataType: 'text', kbnXsrfToken: false, diff --git a/src/plugins/kibana_utils/common/url/encode_uri_query.ts b/src/plugins/kibana_utils/common/url/encode_uri_query.ts index e65a8c2cc1581..fb60f0ceff10f 100644 --- a/src/plugins/kibana_utils/common/url/encode_uri_query.ts +++ b/src/plugins/kibana_utils/common/url/encode_uri_query.ts @@ -17,7 +17,8 @@ * under the License. */ -import { isUndefined, transform } from 'lodash'; +import { ParsedQuery } from 'query-string'; +import { transform } from 'lodash'; /** * This method is intended for encoding *key* or *value* parts of query component. We need a custom @@ -41,11 +42,16 @@ export function encodeUriQuery(val: string, pctEncodeSpaces = false) { } export const encodeQuery = ( - query: Record, + query: ParsedQuery, encodeFunction: (val: string, pctEncodeSpaces?: boolean) => string = encodeUriQuery ) => transform(query, (result, value, key) => { if (key) { - result[key] = encodeFunction(isUndefined(value) ? '' : value, true); + const singleValue = Array.isArray(value) ? value.join(',') : value; + + result[key] = encodeFunction( + singleValue === undefined || singleValue === null ? '' : singleValue, + true + ); } }); diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 18d4cd502f284..2912b665ff014 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -18,13 +18,13 @@ */ import { format as formatUrl } from 'url'; -import { stringify } from 'query-string'; +import { stringify, ParsedQuery } from 'query-string'; import { parseUrl, parseUrlHash } from './parse'; import { url as urlUtils } from '../../../common'; export function replaceUrlHashQuery( rawUrl: string, - queryReplacer: (query: Record) => Record + queryReplacer: (query: ParsedQuery) => ParsedQuery ) { const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); diff --git a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts index 2e0d3b4f2cc7f..d128dc432e9cf 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/modify_url.ts @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; /** @@ -18,7 +20,7 @@ export interface URLMeaningfulParts { protocol?: string | null; slashes?: boolean | null; port?: string | null; - query: Record; + query: ParsedQuery; } /** diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts index ad1dbd676e05b..284af62e52fbb 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts @@ -85,7 +85,7 @@ export const useUrlState = ({ return [state, setState] as [typeof state, typeof setState]; }; -const decodeRisonUrlState = (value: string | undefined): RisonValue | undefined => { +const decodeRisonUrlState = (value: string | undefined | null): RisonValue | undefined => { try { return value ? decode(value) : undefined; } catch (error) { @@ -100,8 +100,8 @@ const encodeRisonUrlState = (state: any) => encode(state); const getQueryStringFromLocation = (location: Location) => location.search.substring(1); -const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = parse(queryString, { sort: false }); +const getParamFromQueryString = (queryString: string, key: string) => { + const parsedQueryString = parse(queryString, { sort: false }); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 473e7ebb646b1..5325fad25e742 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -32,7 +32,7 @@ function isRisonSerializationRequired(queryParam: string): boolean { export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; - const parsedQueryString: Record = parse(search, { sort: false }); + const parsedQueryString: Record = parse(search, { sort: false }); try { Object.keys(parsedQueryString).forEach(a => { diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 2805d8edf94f5..ff133ed79ad0f 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -42,8 +42,8 @@ export const encodeRisonUrlState = (state: any) => encode(state); export const getQueryStringFromLocation = (search: string) => search.substring(1); -export const getParamFromQueryString = (queryString: string, key: string): string | undefined => { - const parsedQueryString: Record = parse(queryString, { sort: false }); +export const getParamFromQueryString = (queryString: string, key: string) => { + const parsedQueryString = parse(queryString, { sort: false }); const queryParam = parsedQueryString[key]; return Array.isArray(queryParam) ? queryParam[0] : queryParam; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts index 9c451db0ed815..dc309943d7cf9 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts @@ -23,7 +23,7 @@ export const useUrlParams: UptimeUrlParamsHook = () => { search = location.search; } - const params: Record = search + const params = search ? parse(search[0] === '?' ? search.slice(1) : search, { sort: false }) : {}; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts index f01448d9e37ac..11dfc3f21b1bf 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts @@ -46,7 +46,7 @@ const { * require further development. */ export const getSupportedUrlParams = (params: { - [key: string]: string | string[] | undefined; + [key: string]: string | string[] | undefined | null; }): UptimeUrlParams => { const filteredParams: { [key: string]: string | undefined } = {}; Object.keys(params).forEach(key => { diff --git a/x-pack/plugins/spaces/server/lib/utils/url.ts b/x-pack/plugins/spaces/server/lib/utils/url.ts index 47dee87686ddf..c91934bb99f1f 100644 --- a/x-pack/plugins/spaces/server/lib/utils/url.ts +++ b/x-pack/plugins/spaces/server/lib/utils/url.ts @@ -8,6 +8,7 @@ // DIRECT COPY FROM `src/core/utils/url`, since it's not possible to import from there, // nor can I re-export from `src/core/server`... +import { ParsedQuery } from 'query-string'; import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; export interface URLMeaningfulParts { @@ -18,7 +19,7 @@ export interface URLMeaningfulParts { protocol: string | null; slashes: boolean | null; port: string | null; - query: Record | {}; + query: ParsedQuery | {}; } /** From 293a121121e0e63b95facd9541966d47d8bee8b1 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 11 Feb 2020 22:28:10 +0300 Subject: [PATCH 17/18] Update src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx Co-Authored-By: Luke Elmers --- .../containers/editor/legacy/console_editor/editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 65fb4c9fe19e3..bfa74392c14fb 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -106,7 +106,7 @@ function EditorUI({ initialTextValue }: EditorProps) { if (/^https?:\/\//.test(url)) { const loadFrom: Record = { url, - // Having dataType here i\s required as it doesn't allow jQuery to `eval` content + // Having dataType here is required as it doesn't allow jQuery to `eval` content // coming from the external source thereby preventing XSS attack. dataType: 'text', kbnXsrfToken: false, From 1211d3dc69aabc4c0801328ba737889815de9fe2 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 12 Feb 2020 18:12:25 +0300 Subject: [PATCH 18/18] add more tests for APM --- .../shared/Links/url_helpers.test.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx index ac728e72fa877..286af610707e1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx @@ -16,6 +16,44 @@ describe('toQuery', () => { }); describe('fromQuery', () => { + it('should not encode the following characters', () => { + expect( + fromQuery({ + a: true, + b: 5000, + c: ':' + }) + ).toEqual('a=true&b=5000&c=:'); + }); + + it('should encode the following characters', () => { + expect( + fromQuery({ + a: '@', + b: '.', + c: ';', + d: ' ' + }) + ).toEqual('a=%40&b=.&c=%3B&d=%20'); + }); + + it('should handle null and undefined', () => { + expect( + fromQuery({ + a: undefined, + b: null + }) + ).toEqual('a=&b='); + }); + + it('should handle arrays', () => { + expect( + fromQuery({ + arr: ['a', 'b'] + }) + ).toEqual('arr=a%2Cb'); + }); + it('should parse object to string', () => { expect( fromQuery({