From bf2edeb6cf7dafb96c59c51b638322a3147160a3 Mon Sep 17 00:00:00 2001 From: AleksandrRogov Date: Mon, 9 Sep 2024 22:16:28 -0400 Subject: [PATCH] refactoring; removing duplicate code --- src/helpers/Regex.ts | 29 ++++++++++++++++++++++++++--- src/utils/Request.ts | 26 +++++++++++++++----------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/helpers/Regex.ts b/src/helpers/Regex.ts index 383d414..9867e16 100644 --- a/src/helpers/Regex.ts +++ b/src/helpers/Regex.ts @@ -6,7 +6,7 @@ export const UUID_REGEX = new RegExp(UUID, "i"); export const EXTRACT_UUID_REGEX = new RegExp("^{?(" + UUID + ")}?$", "i"); export const EXTRACT_UUID_FROM_URL_REGEX = new RegExp("(" + UUID + ")\\)$", "i"); //global here is fine because the state is reset inside string.replace function -export const REMOVE_BRACKETS_FROM_GUID_REGEX = new RegExp(`{(${UUID})}`, "g"); +export const REMOVE_BRACKETS_FROM_UUID_REGEX = new RegExp(`{(${UUID})}`, "g"); export const ENTITY_UUID_REGEX = new RegExp(`\\/(\\w+)\\((${UUID})`, "i"); export function isUuid(value: string): boolean { @@ -26,14 +26,21 @@ export function extractUuidFromUrl(url?: string): string | null { } export function removeCurlyBracketsFromUuid(value: string): string { - return value.replace(REMOVE_BRACKETS_FROM_GUID_REGEX, (_match, p1) => p1); + return value.replace(REMOVE_BRACKETS_FROM_UUID_REGEX, (_match, p1) => p1); } +const QUOTATION_MARK_REGEX = /(["'].*?["'])/; + +/** + * Safely removes curly brackets from guids in a URL + * @param url URL to remove curly brackets from + * @returns URL with guid without curly brackets + */ export function safelyRemoveCurlyBracketsFromUrl(url: string): string { //todo: in future I will need to replace this with a negative lookbehind and lookahead // Split the filter string by quotation marks - const parts = url.split(/(["'].*?["'])/); + const parts = url.split(QUOTATION_MARK_REGEX); return parts .map((part, index) => { // Only process parts that are not within quotes @@ -90,9 +97,25 @@ function sanitizeCookie(cookie: string): string { return cookie.replace(SPECIAL_CHARACTER_REGEX, (char) => characterMap[char]); } +const LEADING_SLASH_REGEX = /^\//; +export function removeLeadingSlash(value: string): string { + return value.replace(LEADING_SLASH_REGEX, ""); +} + +const UNICODE_SYMBOLS_REGEX = /[\u007F-\uFFFF]/g; +export function escapeUnicodeSymbols(value: string): string { + return value.replace(UNICODE_SYMBOLS_REGEX, (chr: string) => `\\u${("0000" + chr.charCodeAt(0).toString(16)).slice(-4)}`); +} + +const DOUBLE_QUOTE_REGEX = /"/g; +export function removeDoubleQuotes(value: string): string { + return value.replace(DOUBLE_QUOTE_REGEX, ""); +} + export const BATCH_RESPONSE_HEADERS_REGEX = /^([^()<>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/; export const HTTP_STATUS_REGEX = /HTTP\/?\s*[\d.]*\s+(\d{3})\s+([\w\s]*)$/m; export const CONTENT_TYPE_PLAIN_REGEX = /Content-Type: text\/plain/i; export const ODATA_ENTITYID_REGEX = /OData-EntityId.+/i; export const TEXT_REGEX = /\w+$/g; export const LINE_ENDING_REGEX = /\r?\n/; +export const SEARCH_FOR_ENTITY_NAME_REGEX = /(\w+)(\([\d\w-]+\))$/; diff --git a/src/utils/Request.ts b/src/utils/Request.ts index 26314fc..812989d 100644 --- a/src/utils/Request.ts +++ b/src/utils/Request.ts @@ -4,7 +4,14 @@ import { Utility } from "./Utility"; import { Config, HeaderCollection } from "../dynamics-web-api"; import { ErrorHelper } from "../helpers/ErrorHelper"; import { InternalConfig } from "./Config"; -import { safelyRemoveCurlyBracketsFromUrl } from "../helpers/Regex"; +import { + removeCurlyBracketsFromUuid, + removeLeadingSlash, + escapeUnicodeSymbols, + safelyRemoveCurlyBracketsFromUrl, + SEARCH_FOR_ENTITY_NAME_REGEX, + removeDoubleQuotes, +} from "../helpers/Regex"; export let entityNames: Record | null = null; @@ -339,9 +346,9 @@ export const composePreferHeader = (request: InternalRequest, config: Config): s if (trimmedItem === "return=representation") { returnRepresentation = true; } else if (trimmedItem.includes("odata.include-annotations=")) { - includeAnnotations = trimmedItem.replace("odata.include-annotations=", "").replace(/"/g, ""); + includeAnnotations = removeDoubleQuotes(trimmedItem.replace("odata.include-annotations=", "")); } else if (trimmedItem.startsWith("odata.maxpagesize=")) { - maxPageSize = Number(trimmedItem.replace("odata.maxpagesize=", "").replace(/"/g, "")) || 0; + maxPageSize = Number(removeDoubleQuotes(trimmedItem.replace("odata.maxpagesize=", ""))) || 0; } else if (trimmedItem.includes("odata.track-changes")) { trackChanges = true; } else if (trimmedItem.includes("odata.continue-on-error")) { @@ -491,15 +498,12 @@ export const processData = (data: any, config: InternalConfig): string | Uint8Ar if (data instanceof Uint8Array || data instanceof Uint16Array || data instanceof Uint32Array) return data; - const replaceGuidBrackets = (value: string): string => value.replace(/(.+)\(\{([\w\d-]+)\}\)/g, "$1($2)"); - const replaceEntityNameWithCollectionName = (value: string): string => { - const regularExpression = /([\w_]+)(\([\d\w-]+\))$/; - const valueParts = regularExpression.exec(value); + const valueParts = SEARCH_FOR_ENTITY_NAME_REGEX.exec(value); if (valueParts && valueParts.length > 2) { const collectionName = findCollectionName(valueParts[1]); if (!Utility.isNull(collectionName)) { - return value.replace(regularExpression, `${collectionName}$2`); + return value.replace(SEARCH_FOR_ENTITY_NAME_REGEX, `${collectionName}$2`); } } return value; @@ -512,7 +516,7 @@ export const processData = (data: any, config: InternalConfig): string | Uint8Ar value = `/${value}`; } } else { - value = `${config.dataApi.url}${value.replace(/^\//, "")}`; + value = `${config.dataApi.url}${removeLeadingSlash(value)}`; } } return value; @@ -521,7 +525,7 @@ export const processData = (data: any, config: InternalConfig): string | Uint8Ar const stringifiedData = JSON.stringify(data, (key, value) => { if (key.endsWith("@odata.bind") || key.endsWith("@odata.id")) { if (typeof value === "string" && !value.startsWith("$")) { - value = replaceGuidBrackets(value); + value = removeCurlyBracketsFromUuid(value); if (config.useEntityNames) { value = replaceEntityNameWithCollectionName(value); } @@ -533,7 +537,7 @@ export const processData = (data: any, config: InternalConfig): string | Uint8Ar return value; }); - return stringifiedData.replace(/[\u007F-\uFFFF]/g, (chr: string) => `\\u${("0000" + chr.charCodeAt(0).toString(16)).slice(-4)}`); + return escapeUnicodeSymbols(stringifiedData); }; export const setStandardHeaders = (headers: HeaderCollection = {}): HeaderCollection => {