UNPKG

@sitecore-jss/sitecore-jss

Version:

This module is provided as a part of Sitecore JavaScript Rendering SDK. It contains the core JSS APIs (layout service) and utilities.

183 lines (182 loc) • 8.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RestLayoutService = void 0; const layout_service_1 = require("./layout-service"); const native_fetcher_1 = require("../native-fetcher"); const data_fetcher_1 = require("../data-fetcher"); const debug_1 = __importDefault(require("../debug")); /** * Fetch layout data using the Sitecore Layout Service REST API. * Uses NativeDataFetcher as the default data fetcher (@see NativeDataFetcher). * @augments LayoutServiceBase */ class RestLayoutService extends layout_service_1.LayoutServiceBase { constructor(serviceConfig) { super(); this.serviceConfig = serviceConfig; /** * Provides fetch options in order to fetch data * @param {string} [language] language will be applied to `sc_lang` param * @returns {FetchOptions} fetch options */ this.getFetchParams = (language) => { var _a; return { sc_apikey: this.serviceConfig.apiKey, sc_site: this.serviceConfig.siteName, sc_lang: language || '', tracking: (_a = this.serviceConfig.tracking) !== null && _a !== void 0 ? _a : true, }; }; this.getFetcher = (req, res) => { return this.serviceConfig.dataFetcherResolver ? this.serviceConfig.dataFetcherResolver(req, res) : this.getDefaultFetcher(req, res); }; /** * Returns a fetcher function pre-configured with headers from the incoming request. * Provides default @see NativeDataFetcher data fetcher * @param {IncomingMessage} [req] Request instance * @param {ServerResponse} [res] Response instance * @returns default fetcher */ this.getDefaultFetcher = (req, res) => { const config = { debugger: debug_1.default.layout }; let headers; if (req) { headers = this.setupReqHeaders(req); } const nativeFetcher = new native_fetcher_1.NativeDataFetcher(config); return (url, data) => __awaiter(this, void 0, void 0, function* () { const response = yield nativeFetcher.fetch(url, Object.assign(Object.assign({}, data), { headers })); if (res) { this.setupResHeaders(res, response); } return response; }); }; } /** * Fetch layout data for an item. * @param {string} itemPath item path to fetch layout data for. * @param {string} [language] the language to fetch layout data for. * @param {IncomingMessage} [req] Request instance * @param {ServerResponse} [res] Response instance * @returns {Promise<LayoutServiceData>} layout service data * @throws {Error} the item with the specified path is not found */ fetchLayoutData(itemPath, language, req, res) { return __awaiter(this, void 0, void 0, function* () { var _a; const querystringParams = this.getFetchParams(language); debug_1.default.layout('fetching layout data for %s %s %s', itemPath, language, this.serviceConfig.siteName); const fetcher = this.getFetcher(req, res); const fetchUrl = this.resolveLayoutServiceUrl('render'); try { return yield (0, data_fetcher_1.fetchData)(fetchUrl, fetcher, Object.assign({ item: itemPath }, querystringParams)); } catch (error) { if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { // Aligned with response of GraphQL Layout Service in case if layout is not found. // When 404 Rest Layout Service returns // { // sitecore: { // context: { // pageEditing: false, // language // }, // route: null // }, // } // return error.response.data; } throw error; } }); } /** * Fetch layout data for a particular placeholder. * Makes a request to Sitecore Layout Service for the specified placeholder in * a specific route item. Allows you to retrieve rendered data for individual placeholders instead of entire routes. * @param {string} placeholderName the name of the placeholder to fetch layout data for. * @param {string} itemPath the path to the item to fetch layout data for. * @param {string} [language] the language to fetch data for. * @param {IncomingMessage} [req] Request instance * @param {ServerResponse} [res] Response instance * @returns {Promise<PlaceholderData>} placeholder data */ fetchPlaceholderData(placeholderName, itemPath, language, req, res) { const querystringParams = this.getFetchParams(language); debug_1.default.layout('fetching placeholder data for %s %s %s %s', placeholderName, itemPath, language, this.serviceConfig.siteName); const fetcher = this.serviceConfig.dataFetcherResolver ? this.serviceConfig.dataFetcherResolver(req, res) : this.getDefaultFetcher(req, res); const fetchUrl = this.resolveLayoutServiceUrl('placeholder'); return (0, data_fetcher_1.fetchData)(fetchUrl, fetcher, Object.assign({ placeholderName, item: itemPath }, querystringParams)); } /** * Resolves layout service url * @param {string} apiType which layout service API to call ('render' or 'placeholder') * @returns the layout service url */ resolveLayoutServiceUrl(apiType) { const { apiHost = '', configurationName = 'jss' } = this.serviceConfig; return `${apiHost}/sitecore/api/layout/${apiType}/${configurationName}`; } /** * Creates an HTTP `Headers` object populated with headers from the incoming request. * @param {IncomingMessage} [req] - The incoming HTTP request, used to extract headers. * @returns {Headers} - An instance of the `Headers` object populated with the extracted headers. */ setupReqHeaders(req) { const headers = new Headers(); if (req === null || req === void 0 ? void 0 : req.headers) { // Copy all headers from req.headers Object.entries(req.headers).forEach(([key, value]) => { if (value) { headers.set(key, Array.isArray(value) ? value.join(', ') : value); } }); // Add or override specific headers req.headers.cookie && headers.set('cookie', req.headers.cookie); req.headers.referer && headers.set('referer', req.headers.referer); req.headers['user-agent'] && headers.set('user-agent', req.headers['user-agent']); req.socket.remoteAddress && headers.set('X-Forwarded-For', req.socket.remoteAddress); } return headers; } /** * Setup response headers based on response from layout service * @param {ServerResponse} res Response instance * @param {NativeDataFetcherResponse<T>} serverRes * @returns {NativeDataFetcherResponse} response */ setupResHeaders(res, serverRes) { debug_1.default.layout('performing response header passing'); const headers = serverRes.headers; if (headers instanceof Headers && headers.has('set-cookie')) { const rawSetCookie = headers.get('set-cookie'); if (rawSetCookie) { const cookies = rawSetCookie.includes(', ') && rawSetCookie.includes(';') ? rawSetCookie.split(/,(?=\s*\w+=)/) : [rawSetCookie]; res.setHeader('Set-Cookie', cookies); } } return serverRes; } } exports.RestLayoutService = RestLayoutService;