"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPrismPathFromRequestUrl = exports.RequestMakerStore = void 0;
const tslib_1 = require("tslib");
const PrismClient = tslib_1.__importStar(require("@stoplight/prism-http/dist/client"));
const axios_1 = tslib_1.__importDefault(require("axios"));
const lodash_1 = require("lodash");
const mobx_1 = require("mobx");
const parse_prefer_header_1 = tslib_1.__importDefault(require("parse-prefer-header"));
const urijs_1 = tslib_1.__importDefault(require("urijs"));
const getOperationData_1 = require("../../utils/getOperationData");
const headers_1 = require("../../utils/headers");
const isAxiosError_1 = require("../../utils/isAxiosError");
const request_1 = require("./request");
const response_1 = require("./response");
mobx_1.configure({ enforceActions: 'observed' });
const defaultPrismConfig = {
    mock: { dynamic: false },
    checkSecurity: true,
    validateRequest: true,
    validateResponse: false,
    errors: false,
};
let RequestMakerStore = (() => {
    class RequestMakerStore {
        constructor(options) {
            this.isSending = false;
            this.request = new request_1.RequestStore();
            this.response = response_1.ResponseStore.createEmpty();
            this.setOperationData = (operation) => {
                Object.assign(this.request, getOperationData_1.getOperationData(operation));
                this._originalOperation = operation;
                this._originalRequest = this.request.toPartialHttpRequest();
            };
            this.setRequestData = (request) => {
                Object.assign(this.request, lodash_1.pickBy({
                    method: request.method,
                    url: request.url,
                    publicBaseUrl: request.baseUrl,
                    headers: request.headers,
                    body: request.body,
                    query: request.query,
                }, v => v !== undefined && v !== null));
                this._originalRequest = this.request.toPartialHttpRequest();
            };
            this.setPreferHeaderOption = (key, value) => {
                const activePreferHeaders = [...this.activePreferHeaders];
                if (activePreferHeaders.length === 0) {
                    this.request.headerParams.push({
                        name: 'Prefer',
                        value: headers_1.formatMultiValueHeader([key, value]),
                        isEnabled: true,
                    });
                    return;
                }
                const mergedPreferences = parsePreferHeaders(activePreferHeaders);
                mergedPreferences[key] = value;
                const lastActivePreferHeader = activePreferHeaders[activePreferHeaders.length - 1];
                lastActivePreferHeader.value = headers_1.formatMultiValueHeader(...Object.entries(mergedPreferences));
                this.request.headerParams = lodash_1.without(this.request.headerParams, ...activePreferHeaders.slice(0, -1));
            };
            this.removePreferHeaderOption = (key) => {
                const activePreferHeaders = [...this.activePreferHeaders];
                if (activePreferHeaders.length === 0)
                    return;
                const mergedPreferences = parsePreferHeaders(activePreferHeaders);
                const lastActivePreferHeader = activePreferHeaders[activePreferHeaders.length - 1];
                lastActivePreferHeader.value = headers_1.formatMultiValueHeader(...Object.entries(mergedPreferences).filter(entry => entry[0] !== key));
                if (lastActivePreferHeader.value) {
                    this.request.headerParams = lodash_1.without(this.request.headerParams, ...activePreferHeaders.slice(0, -1));
                }
                else {
                    this.request.headerParams = lodash_1.without(this.request.headerParams, ...activePreferHeaders);
                }
            };
            this.setPrismMockingOption = (key, value) => {
                const preferenceKey = key === 'exampleKey' ? 'example' : lodash_1.kebabCase(key);
                if (value === undefined || defaultPrismConfig.mock[key] === value) {
                    this.removePreferHeaderOption(preferenceKey);
                }
                else {
                    this.setPreferHeaderOption(preferenceKey, value.toString());
                }
            };
            this.setPrismConfigurationOption = (key, value) => {
                const preferenceKey = lodash_1.kebabCase(key);
                if (defaultPrismConfig[key] === value) {
                    this.removePreferHeaderOption(preferenceKey);
                }
                else {
                    this.setPreferHeaderOption(preferenceKey, value.toString());
                }
            };
            this.mock = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
                if (!this.operation)
                    return;
                this.isSending = true;
                const time = Date.now();
                let store;
                try {
                    const url = extractPrismPathFromRequestUrl(this.request.url, this.request.baseUrl);
                    const response = yield this.prism.request(url, this.request.toPrism());
                    store = response_1.ResponseStore.fromMockObjectResponse(Object.assign(Object.assign({}, response), { violations: response.violations.output }));
                }
                catch (err) {
                    store = response_1.ResponseStore.fromError(err);
                }
                store.responseTime = Date.now() - time;
                store.originalRequest = this.request.toPartialHttpRequest();
                mobx_1.runInAction(() => {
                    this.response = store;
                    this.isSending = false;
                });
            });
            this.send = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
                this.isSending = true;
                if (this.cancelToken) {
                    this.cancelToken.cancel();
                }
                this.cancelToken = axios_1.default.CancelToken.source();
                const time = Date.now();
                let store;
                try {
                    const response = yield axios_1.default.request(Object.assign(Object.assign({}, this.request.toAxios()), { cancelToken: this.cancelToken.token, timeout: this.request.timeout, responseType: 'arraybuffer' }));
                    store = response_1.ResponseStore.fromNetworkResponse(response);
                }
                catch (err) {
                    if (err.response) {
                        store = response_1.ResponseStore.fromNetworkResponse(err.response);
                    }
                    else if (isAxiosError_1.isAxiosError(err)) {
                        store = response_1.ResponseStore.fromAxiosError(err);
                    }
                    else {
                        store = response_1.ResponseStore.fromError(err);
                    }
                }
                store.responseTime = Date.now() - time;
                store.originalRequest = this.request.toPartialHttpRequest();
                mobx_1.runInAction(() => {
                    this.cancelToken = undefined;
                    this.response = store;
                    this.isSending = false;
                });
            });
            if (options) {
                const { request, operation, validate, mockUrl } = options;
                if (operation) {
                    this.setOperationData(operation);
                }
                if (request) {
                    this.setRequestData(request);
                }
                if (validate) {
                    this.request.validate = validate;
                }
                if (mockUrl) {
                    this.request.mockBaseUrl = mockUrl;
                }
            }
            mobx_1.reaction(() => ({
                isMatchingOperation: this.isMatchingOperation,
                requestStore: this.request,
            }), ({ isMatchingOperation, requestStore }) => {
                if (!isMatchingOperation && requestStore.shouldMock) {
                    requestStore.shouldMock = false;
                }
            });
        }
        get hasChanges() {
            return !lodash_1.isEqual(this._originalRequest, this.request.toPartialHttpRequest());
        }
        get operation() {
            return this._originalOperation;
        }
        set operation(operation) {
            if (operation) {
                this.setOperationData(operation);
            }
        }
        get prism() {
            let operation;
            if (this.request.servers && this.request.servers.length) {
                operation = [lodash_1.merge(this.operation, { servers: this.request.servers })];
            }
            else {
                operation = [this.operation];
            }
            return PrismClient.createClientFromOperations(operation, this.prismConfig);
        }
        get activePreferHeaders() {
            return this.request.headerParams.filter(h => h.isEnabled && h.name.toLowerCase() === 'prefer');
        }
        get prismConfig() {
            var _a, _b, _c, _d, _e;
            const mergedPreferences = parsePreferHeaders(this.activePreferHeaders);
            return {
                mock: {
                    dynamic: (_a = parseOptionalBoolean(mergedPreferences.dynamic)) !== null && _a !== void 0 ? _a : defaultPrismConfig.mock.dynamic,
                    code: mergedPreferences.code,
                    exampleKey: mergedPreferences.example,
                },
                checkSecurity: (_b = parseOptionalBoolean(mergedPreferences['check-security'])) !== null && _b !== void 0 ? _b : defaultPrismConfig.checkSecurity,
                validateRequest: (_c = parseOptionalBoolean(mergedPreferences['validate-request'])) !== null && _c !== void 0 ? _c : defaultPrismConfig.validateRequest,
                validateResponse: (_d = parseOptionalBoolean(mergedPreferences['validate-response'])) !== null && _d !== void 0 ? _d : defaultPrismConfig.validateResponse,
                errors: (_e = parseOptionalBoolean(mergedPreferences.errors)) !== null && _e !== void 0 ? _e : defaultPrismConfig.errors,
            };
            function parseOptionalBoolean(input) {
                if (input === 'true')
                    return true;
                if (input === 'false')
                    return false;
                return undefined;
            }
        }
        get isMatchingOperation() {
            var _a;
            if (this.request.method === ((_a = this.operation) === null || _a === void 0 ? void 0 : _a.method)) {
                return true;
            }
            return false;
        }
        get isMockEnabled() {
            return this.request.shouldMock && this.isMatchingOperation;
        }
        cancel() {
            if (this.cancelToken) {
                this.cancelToken.cancel();
            }
            this.cancelToken = undefined;
            this.isSending = false;
        }
        reset() {
            this.request = new request_1.RequestStore();
            this.response = response_1.ResponseStore.createEmpty();
            if (this._originalOperation) {
                this.setOperationData(this._originalOperation);
            }
            else if (this._originalRequest) {
                this.setRequestData(this._originalRequest);
            }
        }
    }
    tslib_1.__decorate([
        mobx_1.observable.ref,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "cancelToken", void 0);
    tslib_1.__decorate([
        mobx_1.observable,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "isSending", void 0);
    tslib_1.__decorate([
        mobx_1.observable.ref,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "request", void 0);
    tslib_1.__decorate([
        mobx_1.observable.ref,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "response", void 0);
    tslib_1.__decorate([
        mobx_1.observable,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "_originalOperation", void 0);
    tslib_1.__decorate([
        mobx_1.observable,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "_originalRequest", void 0);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "hasChanges", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [Object])
    ], RequestMakerStore.prototype, "operation", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "prism", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "activePreferHeaders", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "prismConfig", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "isMatchingOperation", null);
    tslib_1.__decorate([
        mobx_1.computed,
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [])
    ], RequestMakerStore.prototype, "isMockEnabled", null);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "setOperationData", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "setRequestData", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "setPrismMockingOption", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "setPrismConfigurationOption", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "mock", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Object)
    ], RequestMakerStore.prototype, "send", void 0);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Function),
        tslib_1.__metadata("design:paramtypes", []),
        tslib_1.__metadata("design:returntype", void 0)
    ], RequestMakerStore.prototype, "cancel", null);
    tslib_1.__decorate([
        mobx_1.action,
        tslib_1.__metadata("design:type", Function),
        tslib_1.__metadata("design:paramtypes", []),
        tslib_1.__metadata("design:returntype", void 0)
    ], RequestMakerStore.prototype, "reset", null);
    return RequestMakerStore;
})();
exports.RequestMakerStore = RequestMakerStore;
function parsePreferHeaders(activePreferHeaders) {
    const enabledHeaders = activePreferHeaders.map(h => h.value).map(parse_prefer_header_1.default);
    const mergedPreferences = enabledHeaders.reduce((acc, current) => (Object.assign(Object.assign({}, acc), current)), {});
    return lodash_1.mapValues(lodash_1.mapKeys(mergedPreferences, (_, k) => lodash_1.kebabCase(k)), v => (v === true ? '' : v));
}
function extractPrismPathFromRequestUrl(requestUrl, baseUrl) {
    const requestUri = new urijs_1.default(requestUrl);
    const baseUri = new urijs_1.default(baseUrl);
    const baseUriResource = baseUri.path();
    return requestUri.resource().replace(baseUriResource, baseUriResource.endsWith('/') ? '/' : '');
}
exports.extractPrismPathFromRequestUrl = extractPrismPathFromRequestUrl;
//# sourceMappingURL=index.js.map