diff --git a/package.json b/package.json index c16cc959f..074f30f22 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "jszip": "^3.5.0", "lodash": "^4.17.20", "ol": "^5.3.3", + "path-to-regexp": "^6.2.0", "promise.allsettled": "^1.0.2", "rc-align": "^2.4.5", "rc-checkbox": "^2.1.8", diff --git a/src/common/utils.ts b/src/common/utils.ts index 37f4bb364..5cdaa8422 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -9,6 +9,7 @@ import { useState, useEffect } from 'react'; import {constants} from "./constants"; import _ from "lodash"; import JsZip from 'jszip'; +import { match, compile } from "path-to-regexp"; // tslint:disable-next-line:no-var-requires const tagColors = require("../react/components/common/tagColors.json"); @@ -507,3 +508,41 @@ export function downloadZipFile(data: zipData[], fileName: string): void { fileLink.click(); }); } + +export class URIUtils { + + public static normalizePath(path: string): string { + return "/" + path.replace(/(\r\n|\n|\r)/gm, "").replace(/^\/+/, ""); + } + + public static matchPath(pathTemplate: string, path: string): object { + const matcher = match(pathTemplate, { decode: decodeURIComponent }); + const result = matcher(path) + return (result && result.params) || {}; + } + + public static compilePath(pathTemplate: string, params: object): string { + const toPath = compile(pathTemplate, { encode: encodeURIComponent }); + return toPath(params); + } + + public static composeQueryString(params: object, blacklist = new Set()) { + const kvList = []; + const connector = "&"; + for (const [key, value] of Object.entries(params)) { + if (key && (value === 0 || value) && !blacklist.has(key)) { + kvList.push(`${key}=${value}`); + } + } + return kvList.join(connector); + } + + public static matchQueryString(queryString: string) { + const params = {}; + queryString.split("&").forEach(s => { + const [key, value] = s.split("="); + params[key] = value; + }); + return params; + } +} diff --git a/src/react/components/pages/prebuiltPredict/prebuiltPredictPage.tsx b/src/react/components/pages/prebuiltPredict/prebuiltPredictPage.tsx index bc1fac60b..72c40e2b5 100644 --- a/src/react/components/pages/prebuiltPredict/prebuiltPredictPage.tsx +++ b/src/react/components/pages/prebuiltPredict/prebuiltPredictPage.tsx @@ -45,6 +45,7 @@ import { ITableHelper, ITableState, TableHelper } from "./tableHelper"; import { Toggle } from "office-ui-fabric-react/lib/Toggle"; import { ILayoutHelper, LayoutHelper } from "./layoutHelper"; import HtmlFileReader from "../../../../common/htmlFileReader"; +import {URIUtils} from "../../../../common/utils"; interface IPrebuiltTypes { name: string; @@ -104,21 +105,21 @@ export class PrebuiltPredictPage extends React.Component { - this.setState({ predictionEndpointUrl: this.getUpdateRequestURI() }); - } - - private getUpdateRequestURI = () => { - const { prebuiltSettings } = this.props; - const { currentPrebuiltType, predictionEndpointUrl } = this.state; - let updatedURI = ""; - if (predictionEndpointUrl.includes("?")) { - const queryString = predictionEndpointUrl.split("?")[1]; - if (prebuiltSettings && prebuiltSettings.serviceURI) { - const parameterArray = queryString.includes("&") ? queryString.split("&") : [queryString]; - let newQueryString = ""; - let connector = ""; - for (const parameter of parameterArray) { - const name = parameter.split("=")[0]; - if (name !== "locale" && name !== constants.pages) { - newQueryString += `${connector}${parameter}`; - } - connector = "&"; - } - if (this.state.withPageRange && this.state.pageRangeIsValid) { - newQueryString += `${connector}${constants.pages}=${this.state.pageRange}`; - connector = "&"; + this.setState({ predictionEndpointUrl: this.getUpdatedRequestURI() }); + } + + private getUpdatedRequestURI = (fromTextArea: boolean = false) => { + const { predictionEndpointUrl } = this.state; + const [path, queryString] = predictionEndpointUrl.split("?"); + const newPath = this.getUpdatedPath(path, fromTextArea); + const newQueryString = this.getUpdatedQueryString(queryString); + return `${newPath}?${newQueryString}`; + } + + private getUpdatedPath(path: string, fromTextArea: boolean): string { + const pathTemplate = "/formrecognizer/:prebuiltServiceVersion/prebuilt/:prebuiltType/analyze"; + const normalizedPath = URIUtils.normalizePath(path); + const pathParams = URIUtils.matchPath(pathTemplate, normalizedPath); + if (fromTextArea) { + const prebuiltType = _.get(pathParams, "prebuiltType", ""); + if (prebuiltType && prebuiltType !== this.state.currentPrebuiltType.servicePath) { + const ret = this.prebuiltTypes.filter(item => item.servicePath === prebuiltType); + if (ret.length === 1) { + this.setState({ currentPrebuiltType: ret[0] }); } - if (this.state.currentPrebuiltType.useLocale) { - newQueryString += `${connector}locale=${this.state.currentLocale}`; + } + } else { + pathParams["prebuiltType"] = this.state.currentPrebuiltType.servicePath; + } + + return URIUtils.compilePath(pathTemplate, pathParams); + } + + private getUpdatedQueryString(queryString: string): string { + let newQueryString = ""; + if (queryString) { + const parameterArray = queryString.includes("&") ? queryString.split("&") : [queryString]; + let connector = ""; + for (const parameter of parameterArray) { + const name = parameter.split("=")[0]; + if (name !== "locale" && name !== constants.pages) { + newQueryString += `${connector}${parameter}`; } - updatedURI = `/formrecognizer/${constants.prebuiltServiceVersion}${currentPrebuiltType.servicePath}?${newQueryString}`; + connector = "&"; + } + if (this.state.withPageRange && this.state.pageRangeIsValid) { + newQueryString += `${connector}${constants.pages}=${this.state.pageRange}`; + connector = "&"; + } + if (this.state.currentPrebuiltType.useLocale) { + newQueryString += `${connector}locale=${this.state.currentLocale}`; } } else { - let apiRequest = `/formrecognizer/${constants.prebuiltServiceVersion}${this.state.currentPrebuiltType.servicePath}?includeTextDetails=true`; + newQueryString = `includeTextDetails=true`; if (this.state.withPageRange && this.state.pageRangeIsValid) { - apiRequest += `&${constants.pages}=${this.state.pageRange}`; + newQueryString += `&${constants.pages}=${this.state.pageRange}`; } - updatedURI = apiRequest + (this.state.currentPrebuiltType.useLocale ? `&locale=${this.state.currentLocale}` : ""); + newQueryString += this.state.currentPrebuiltType.useLocale ? `&locale=${this.state.currentLocale}` : ""; } - return updatedURI; + return newQueryString; } private getComposedURL = () => { - const uri = this.getUpdateRequestURI(); + const uri = this.getUpdatedRequestURI(true); return this.props.prebuiltSettings.serviceURI.replace(/\/+$/, "") + "/" + uri.replace(/(\r\n|\n|\r)/gm, "").replace(/^\/+/, ""); } diff --git a/yarn.lock b/yarn.lock index 6f5e28e17..b8eab85e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9461,6 +9461,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" + integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"