| 173 | } |
| 174 | |
| 175 | function buildOdataUrl(req: ProxyRequest, pathOverride?: string): string { |
| 176 | const host = resolveHost(req) |
| 177 | const servicePath = `/sap/opu/odata/sap/${req.service}` |
| 178 | const subPath = pathOverride ?? req.path |
| 179 | const normalized = subPath.startsWith('/') ? subPath : `/${subPath}` |
| 180 | const base = `${host}${servicePath}${normalized}` |
| 181 | |
| 182 | if (pathOverride !== undefined) { |
| 183 | return base |
| 184 | } |
| 185 | if (!req.query || Object.keys(req.query).length === 0) { |
| 186 | return base |
| 187 | } |
| 188 | const encode = (s: string) => encodeURIComponent(s).replace(/%24/g, '$') |
| 189 | const parts: string[] = [] |
| 190 | for (const [key, value] of Object.entries(req.query)) { |
| 191 | if (value === undefined || value === null) continue |
| 192 | parts.push(`${encode(key)}=${encode(String(value))}`) |
| 193 | } |
| 194 | const queryString = parts.join('&') |
| 195 | if (!queryString) return base |
| 196 | return base.includes('?') ? `${base}&${queryString}` : `${base}?${queryString}` |
| 197 | } |
| 198 | |
| 199 | const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE', 'MERGE']) |
| 200 | |