UNPKG

@sap-cloud-sdk/odata-common

Version:

SAP Cloud SDK for JavaScript common functions of OData client generator and OpenAPI clint generator.

237 lines • 9.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ODataRequest = void 0; const util_1 = require("@sap-cloud-sdk/util"); const connectivity_1 = require("@sap-cloud-sdk/connectivity"); const http_client_1 = require("@sap-cloud-sdk/http-client"); const internal_1 = require("@sap-cloud-sdk/http-client/internal"); const internal_2 = require("@sap-cloud-sdk/connectivity/internal"); const odata_request_traits_1 = require("./odata-request-traits"); /** * OData request configuration for an entity type. * @template EntityT - Type of the entity to setup a request for. */ class ODataRequest { /** * Creates an instance of ODataRequest. * @param config - Configuration of the request. * @param _destination - Destination to setup the request against. */ constructor(config, _destination) { this.config = config; this._destination = _destination; } set destination(dest) { if (dest) { const sanitized = (0, connectivity_1.sanitizeDestination)(dest); (0, internal_2.assertHttpDestination)(sanitized); this._destination = sanitized; } } get destination() { return this._destination; } /** * Constructs an absolute URL for the given request. * @returns The absolute URL for the request. */ url() { return `${(0, util_1.removeTrailingSlashes)(this.resourceUrl())}${this.config.appendedPaths.join('')}${this.query()}`; } /** * Constructs a URL relative to the destination. * @param includeBasePath - Whether or not to include the base path in the URL. * @param includeQueryParameters - Whether or not to include the query parameters in the URL. * @returns The relative URL for the request. */ relativeUrl(includeBasePath = true, includeQueryParameters = true) { const query = includeQueryParameters ? this.query() : ''; return `${(0, util_1.removeTrailingSlashes)(this.relativeResourceUrl(includeBasePath))}${this.config.appendedPaths.join('')}${query}`; } /** * Specifies whether the destination needs a specific authentication or not. * @returns A boolean value that specifies whether the destination needs authentication or not. */ needsAuthentication() { return (!!this.destination && this.destination.authentication !== 'NoAuthentication'); } /** * Returns the service URL for a given OData request. * @returns The URL of the service the given entity belongs to. */ serviceUrl() { if (!this.destination) { throw Error('The destination is undefined.'); } const systemUrl = this.destination.url; const basePath = typeof this.config.basePath === 'undefined' ? this.config.defaultBasePath : this.config.basePath; return `${(0, util_1.removeTrailingSlashes)(systemUrl)}/${(0, util_1.removeSlashes)(basePath)}`; } /** * Returns the service URL relative to the url of the destination for a given OData request. * @returns The relative URL of the service the given entity belongs to. */ relativeServiceUrl() { const basePath = typeof this.config.basePath === 'undefined' ? this.config.defaultBasePath : this.config.basePath; return `${(0, util_1.removeSlashes)(basePath)}`; } /** * Returns the URL to a specific OData .resource, i.e. the entity collection. * @returns The URL of the resource. */ resourceUrl() { return `${(0, util_1.removeTrailingSlashes)(this.serviceUrl())}/${this.config.resourcePath()}`; } /** * Returns the relative URL to a specific OData resource. * @param includeBasePath - Whether or not to include the base path in the URL. * @returns The relative URL of the resource. */ relativeResourceUrl(includeBasePath = true) { const baseUrl = includeBasePath ? (0, util_1.removeTrailingSlashes)(this.relativeServiceUrl()) : ''; const url = `${baseUrl}/${this.config.resourcePath()}`; return (0, util_1.removeLeadingSlashes)(url); } /** * Get query parameters as string. Leads with `?` if there are parameters to return. * @returns Query parameter string. */ query() { const parameters = (0, internal_1.mergeOptionsWithPriority)(this.queryParameters()); if (parameters) { const query = Object.entries(parameters) .map(([key, value]) => `${key}=${value}`) .join('&'); return query.length ? `?${query}` : ''; } return ''; } /** * Create object containing all headers, including custom headers for the given request. * @returns Key-value pairs where the key is the name of a header property and the value is the respective value. */ async headers() { try { if (!this.destination) { throw Error('The destination is undefined.'); } if (Object.keys(this.customHeaders()).length > 0) { return { custom: this.customHeaders(), requestConfig: { ...this.defaultHeaders(), ...this.eTagHeaders() } }; } return { requestConfig: { ...this.defaultHeaders(), ...this.eTagHeaders() } }; } catch (error) { throw new util_1.ErrorWithCause('Constructing headers for OData request failed!', error); } } /** * Get all custom headers. * @returns Key-value pairs where the key is the name of a header property and the value is the respective value. */ customHeaders() { return this.config.customHeaders; } /** * Get all default headers. If custom headers are set, those take precedence. * @returns Key-value pairs where the key is the name of a header property and the value is the respective value. */ defaultHeaders() { const additionalHeaders = this.getAdditionalHeadersForKeys(...Object.keys(this.config.defaultHeaders)); return (0, util_1.mergeIgnoreCase)((0, util_1.pickNonNullish)(this.config.defaultHeaders), additionalHeaders); } /** * Get the eTag related headers, e.g. `if-match`. * @returns Key-value pairs where the key is the name of a header property and the value is the respective value. */ eTagHeaders() { const additionalIfMatchHeader = this.getAdditionalHeadersForKeys('if-match'); if (Object.keys(additionalIfMatchHeader).length) { return additionalIfMatchHeader; } const eTag = (0, odata_request_traits_1.isWithETag)(this.config) ? this.config.versionIdentifierIgnored ? '*' : this.config.eTag : undefined; return (0, util_1.pickNonNullish)({ 'if-match': eTag }); } /** * Execute the given request and return the according promise. * @returns Promise resolving to the requested data. */ async execute() { if (!this.destination) { throw Error('The destination cannot be undefined.'); } return (0, http_client_1.executeHttpRequest)(this.destination, await this.requestConfig(), { fetchCsrfToken: this.config.fetchCsrfToken }).catch(error => { throw constructError(error, this.config.method, this.serviceUrl()); }); } /** * Get http request config. * @returns Promise of http request config with origin. */ async requestConfig() { const defaultConfig = { headers: await this.headers(), params: this.queryParameters(), url: this.relativeUrl(true, false), method: this.config.method, parameterEncoder: this.config.parameterEncoder, middleware: this.config.middlewares, data: this.config.payload }; return { ...defaultConfig, ...(0, internal_1.filterCustomRequestConfig)(this.config.customRequestConfiguration) }; } getAdditionalHeadersForKeys(...keys) { const destinationHeaders = (0, util_1.pickIgnoreCase)(this.destination?.headers, ...keys); const customHeaders = (0, util_1.pickIgnoreCase)(this.customHeaders(), ...keys); return (0, util_1.mergeIgnoreCase)(destinationHeaders, customHeaders); } queryParameters() { if (Object.keys(this.config.customQueryParameters).length > 0) { return { custom: this.config.customQueryParameters, requestConfig: this.config.queryParameters() }; } return { requestConfig: this.config.queryParameters() }; } } exports.ODataRequest = ODataRequest; function constructError(error, requestMethod, url) { if (error.isAxiosError) { const defaultMessage = `${requestMethod} request to ${url} failed!`; const s4SpecificMessage = (0, util_1.propertyExists)(error, 'response', 'data', 'error') ? messageFromS4ErrorResponse(error) : ''; const message = [defaultMessage, s4SpecificMessage].join(' '); return new util_1.ErrorWithCause(message, error); } return error; } function messageFromS4ErrorResponse(error) { return `${(0, util_1.propertyExists)(error.response.data.error, 'message', 'value') ? error.response.data.error.message.value : ''}${util_1.unixEOL}${JSON.stringify(error.response.data.error)}`; } //# sourceMappingURL=odata-request.js.map