|
| 1 | +import { |
| 2 | + CLOSING_SQUARE_BRACKET_URL_CODE, |
| 3 | + OPENING_SQUARE_BRACKET_URL_CODE, |
| 4 | + type ParamSerialization, |
| 5 | + PIPE_URL_CODE, |
| 6 | + type PrimitiveType, |
| 7 | + SPACE_URL_CODE, |
| 8 | + type SupportedParamType, |
| 9 | +} from './param-serialization'; |
| 10 | + |
| 11 | +/** Specification of the parameter type for parameter deserialization */ |
| 12 | +export type ParamTypeForDeserialization = 'primitive' | 'array' | 'object'; |
| 13 | + |
| 14 | +/** |
| 15 | + * Split parameter elements by delimiter based on the serialization style (with removal of the prefix) |
| 16 | + * @param serializedParamValue serialized parameter value |
| 17 | + * @param paramSerialization parameter serialization |
| 18 | + */ |
| 19 | +function splitParamElements(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 20 | + switch (paramSerialization.style) { |
| 21 | + case 'simple': { |
| 22 | + return serializedParamValue.split(',').map((value) => decodeURIComponent(value)); |
| 23 | + } |
| 24 | + case 'label': { |
| 25 | + // NOTE: Path parameters of style label are prefixed with a '.' |
| 26 | + return serializedParamValue.substring(1).split(paramSerialization.explode ? '.' : ',').map((value) => decodeURIComponent(value)); |
| 27 | + } |
| 28 | + case 'matrix': { |
| 29 | + // NOTE: Path parameters of style matrix and exploded true are prefixed with a ';paramName=' |
| 30 | + // NOTE: Path parameters of style matrix and exploded false are written like this: ';paramName=value1;paramName=value2' |
| 31 | + return paramSerialization.explode |
| 32 | + ? serializedParamValue.substring(1).split(';').map((value) => decodeURIComponent(value.split('=')[1])) |
| 33 | + : serializedParamValue.split('=')[1].split(',').map((value) => decodeURIComponent(value)); |
| 34 | + } |
| 35 | + case 'form': { |
| 36 | + // NOTE: Query parameters of style form and explode true are written like this: 'paramName=value1¶mName=value2¶mName=value3' |
| 37 | + // NOTE: Query parameters of style form and explode false are prefixed with the parameter name and delimited by a ',' |
| 38 | + return paramSerialization.explode |
| 39 | + ? serializedParamValue.split('&').map((value) => decodeURIComponent(value.split('=')[1])) |
| 40 | + : serializedParamValue.split('=')[1].split(',').map((value) => decodeURIComponent(value)); |
| 41 | + } |
| 42 | + case 'spaceDelimited': { |
| 43 | + // NOTE: Query parameters of style spaceDelimited and explode false are prefixed with the parameter name and delimited by the encoded space character |
| 44 | + // Here is an example of the format: 'paramName=value1%20value2%20value3' |
| 45 | + return paramSerialization.explode ? undefined : serializedParamValue.split('=')[1].split(SPACE_URL_CODE).map((value) => decodeURIComponent(value)); |
| 46 | + } |
| 47 | + case 'pipeDelimited': { |
| 48 | + // NOTE: Query parameters of style spaceDelimited and explode false are prefixed with the parameter name and delimited by the encoded pipe character |
| 49 | + // Here is an example of the format: 'paramName=value1%7Cvalue2%7Cvalue3' |
| 50 | + return paramSerialization.explode ? undefined : serializedParamValue.split('=')[1].split(PIPE_URL_CODE).map((value) => decodeURIComponent(value)); |
| 51 | + } |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +function objectFromPairwiseArray(splitObject: string[]) { |
| 56 | + return splitObject.reduce((obj: { [key: string]: PrimitiveType }, currentValue, index, array) => { |
| 57 | + // NOTE: Every other item of the array is a key and the following item is the corresponding value |
| 58 | + if (index % 2 === 0) { |
| 59 | + obj[decodeURIComponent(currentValue)] = decodeURIComponent(array[index + 1]); |
| 60 | + } |
| 61 | + return obj; |
| 62 | + }, {} as { [key: string]: PrimitiveType }); |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Deserialize query parameters of type array |
| 67 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 68 | + * @param serializedParamValue serialized query parameter value |
| 69 | + * @param paramSerialization parameter serialization |
| 70 | + */ |
| 71 | +function deserializeArrayQueryParams(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 72 | + return splitParamElements(serializedParamValue, paramSerialization); |
| 73 | +} |
| 74 | + |
| 75 | +/** |
| 76 | + * Deserialize query parameters of type object |
| 77 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 78 | + * @param serializedParamValue serialized query parameter value |
| 79 | + * @param paramSerialization parameter serialization |
| 80 | + */ |
| 81 | +function deserializeObjectQueryParams(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 82 | + // NOTE: Applies to the exploded styles 'form' and 'deepObject' |
| 83 | + if (paramSerialization.explode && (paramSerialization.style === 'form' || paramSerialization.style === 'deepObject')) { |
| 84 | + return serializedParamValue.split('&').reduce((obj: { [key: string]: PrimitiveType }, serializedProperty) => { |
| 85 | + const [key, value] = serializedProperty.split('='); |
| 86 | + // NOTE: The key of an object in deepObject style is surrounded by opening and closing square brackets |
| 87 | + const objKey = paramSerialization.style === 'deepObject' ? key.split(OPENING_SQUARE_BRACKET_URL_CODE)[1].split(CLOSING_SQUARE_BRACKET_URL_CODE)[0] : key; |
| 88 | + obj[decodeURIComponent(objKey)] = decodeURIComponent(value); |
| 89 | + return obj; |
| 90 | + }, {} as { [key: string]: PrimitiveType }); |
| 91 | + } |
| 92 | + |
| 93 | + // NOTE: Applies to the non-exploded styles 'form', 'spaceDelimited', and 'pipeDelimited' |
| 94 | + if (paramSerialization.style !== 'deepObject' && !paramSerialization.explode) { |
| 95 | + // NOTE: The splitParamElements function is called since object query parameters can be split by delimiters based on the serialization style |
| 96 | + // NOTE: The deserialized value will exist since these exploded styles are supported |
| 97 | + const splitObject = splitParamElements(serializedParamValue, paramSerialization); |
| 98 | + return objectFromPairwiseArray(splitObject!); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +/** |
| 103 | + * Deserialize query parameters based on the values of exploded and style and the parameter type |
| 104 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 105 | + * @param serializedQueryParams serialized query parameters |
| 106 | + * @param queryParamSerialization query parameter serialization |
| 107 | + */ |
| 108 | +export function deserializeQueryParams<T extends { [key: string]: string }>( |
| 109 | + serializedQueryParams: T, |
| 110 | + queryParamSerialization: { [p in keyof T]: ParamSerialization & { paramType: ParamTypeForDeserialization } } |
| 111 | +): { [p in keyof T]: SupportedParamType } { |
| 112 | + return Object.entries(serializedQueryParams).reduce((acc, [queryParamName, serializedParamValue]) => { |
| 113 | + const paramSerialization = queryParamSerialization[queryParamName]; |
| 114 | + let deserializedValue: SupportedParamType; |
| 115 | + if (paramSerialization.paramType === 'array') { |
| 116 | + deserializedValue = deserializeArrayQueryParams(serializedParamValue, paramSerialization); |
| 117 | + } else if (paramSerialization.paramType === 'object') { |
| 118 | + deserializedValue = deserializeObjectQueryParams(serializedParamValue, paramSerialization); |
| 119 | + } else { |
| 120 | + // NOTE: Query parameters of type primitive are prefixed with the parameter name like this: 'paramName=value' |
| 121 | + deserializedValue = decodeURIComponent(serializedParamValue.split('=')[1]); |
| 122 | + } |
| 123 | + if (deserializedValue) { |
| 124 | + acc[queryParamName as keyof T] = deserializedValue; |
| 125 | + } |
| 126 | + return acc; |
| 127 | + }, {} as { [p in keyof T]: SupportedParamType }); |
| 128 | +} |
| 129 | + |
| 130 | +/** |
| 131 | + * Deserialize path parameters of type primitive |
| 132 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 133 | + * @param serializedParamValue serialized path parameter value |
| 134 | + * @param paramSerialization parameter serialization |
| 135 | + */ |
| 136 | +function deserializePrimitivePathParams(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 137 | + switch (paramSerialization.style) { |
| 138 | + case 'simple': { |
| 139 | + return decodeURIComponent(serializedParamValue); |
| 140 | + } |
| 141 | + case 'label': { |
| 142 | + // NOTE: Path parameters of style label are prefixed with a '.' |
| 143 | + return decodeURIComponent(serializedParamValue.substring(1)); |
| 144 | + } |
| 145 | + case 'matrix': { |
| 146 | + return decodeURIComponent(serializedParamValue.substring(1).split('=')[1]); |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +/** |
| 152 | + * Deserialize path parameters of type array |
| 153 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 154 | + * @param serializedParamValue serialized path parameter value |
| 155 | + * @param paramSerialization parameter serialization |
| 156 | + */ |
| 157 | +function deserializeArrayPathParams(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 158 | + return splitParamElements(serializedParamValue, paramSerialization); |
| 159 | +} |
| 160 | + |
| 161 | +/** |
| 162 | + * Deserialize path parameters of type object |
| 163 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 164 | + * @param serializedParamValue serialized path parameter value |
| 165 | + * @param paramSerialization parameter serialization |
| 166 | + */ |
| 167 | +function deserializeObjectPathParams(serializedParamValue: string, paramSerialization: ParamSerialization) { |
| 168 | + // NOTE: The splitParamElements function is called since object path parameters can be split by delimiters based on the serialization style |
| 169 | + // NOTE: There is an exception for path parameters of exploded style 'matrix' which is not serialized like its corresponding array |
| 170 | + // This exception is serialized like this (prefixed by a ';'): ';prop1=value1;prop2=value2' |
| 171 | + const splitObject: string[] = paramSerialization.style === 'matrix' && paramSerialization.explode |
| 172 | + ? serializedParamValue.substring(1).split(';').map((value) => decodeURIComponent(value)) |
| 173 | + : splitParamElements(serializedParamValue, paramSerialization)!; |
| 174 | + |
| 175 | + // NOTE: Object path parameters that are exploded are serialized as 'prop=value' |
| 176 | + if (paramSerialization.explode) { |
| 177 | + return splitObject.reduce((obj: { [key: string]: PrimitiveType }, serializedProperty) => { |
| 178 | + const [key, value] = serializedProperty.split('=').map((v) => decodeURIComponent(v)); |
| 179 | + obj[key] = value; |
| 180 | + return obj; |
| 181 | + }, {} as { [key: string]: PrimitiveType }); |
| 182 | + } |
| 183 | + |
| 184 | + return objectFromPairwiseArray(splitObject); |
| 185 | +} |
| 186 | + |
| 187 | +/** |
| 188 | + * Deserialize path parameters based on the values of exploded and style and the parameter type |
| 189 | + * OpenAPI Parameter Serialization {@link https://swagger.io/specification | documentation} |
| 190 | + * @param serializedPathParams serialized path parameters |
| 191 | + * @param pathParamSerialization path parameter serialization |
| 192 | + */ |
| 193 | +export function deserializePathParams<T extends { [key: string]: string }>( |
| 194 | + serializedPathParams: T, |
| 195 | + pathParamSerialization: { [p in keyof T]: ParamSerialization & { paramType: ParamTypeForDeserialization } } |
| 196 | +): { [p in keyof T]: SupportedParamType } { |
| 197 | + return Object.entries(serializedPathParams).reduce((acc, [pathParamName, serializedParamValue]) => { |
| 198 | + const paramSerialization = pathParamSerialization[pathParamName]; |
| 199 | + let deserializedValue: SupportedParamType; |
| 200 | + if (paramSerialization.paramType === 'array') { |
| 201 | + deserializedValue = deserializeArrayPathParams(serializedParamValue, paramSerialization); |
| 202 | + } else if (paramSerialization.paramType === 'object') { |
| 203 | + deserializedValue = deserializeObjectPathParams(serializedParamValue, paramSerialization); |
| 204 | + } else { |
| 205 | + deserializedValue = deserializePrimitivePathParams(serializedParamValue, paramSerialization); |
| 206 | + } |
| 207 | + if (deserializedValue) { |
| 208 | + acc[pathParamName as keyof T] = deserializedValue; |
| 209 | + } |
| 210 | + return acc; |
| 211 | + }, {} as { [p in keyof T]: SupportedParamType }); |
| 212 | +} |
0 commit comments