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());
    });
};
import Debug from 'debug';
import { HasuraApi } from '@deep-foundation/hasura/api.js';
import { generateApolloClient } from '@deep-foundation/hasura/client.js';
import { gql } from '@apollo/client/index.js';
import { serializeError } from 'serialize-error';
import { DeepClient } from '../client.js';
import { ContainerController } from '../container-controller.js';
import { ALLOWED_IDS, DENIED_IDS } from '../global-ids.js';
import { reject, resolve } from '../promise.js';
import { promisify } from 'util';
import { exec } from 'child_process';
import waitOn from 'wait-on';
import { buildClientSchema, getIntrospectionQuery, printSchema } from 'graphql';
const execAsync = promisify(exec);
console.log('process.env', process.env);
const SCHEMA = 'public';
const debug = Debug('deeplinks:eh:links');
const log = debug.extend('log');
const error = debug.extend('error');
const PORT = process.env.PORT || 3006;
const DOCKER_DEEPLINKS_URL = process.env.DOCKER_DEEPLINKS_URL || 'http://host.docker.internal:3006';
const DEEPLINKS_ROUTE_HANDLERS_HOST = process.env.DEEPLINKS_ROUTE_HANDLERS_HOST || 'host.docker.internal';
const deeplinksUrl = process.env.NEXT_PUBLIC_DEEPLINKS_URL;
export const DOCKER = process.env.DOCKER || '0';
const delay = time => new Promise(res => setTimeout(res, time));
const toJSON = (data) => JSON.stringify(data, Object.getOwnPropertyNames(data), 2);
export const api = new HasuraApi({
    path: process.env.DEEPLINKS_HASURA_PATH,
    ssl: !!+process.env.DEEPLINKS_HASURA_SSL,
    secret: process.env.DEEPLINKS_HASURA_SECRET,
});
const client = generateApolloClient({
    path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`,
    ssl: !!+process.env.DEEPLINKS_HASURA_SSL,
    secret: process.env.DEEPLINKS_HASURA_SECRET,
});
const deep = new DeepClient({
    apolloClient: client,
});
let mainPort;
const getMainPort = () => __awaiter(void 0, void 0, void 0, function* () {
    try {
        if (!mainPort)
            mainPort = yield deep.id('@deep-foundation/main-port', 'port');
        return mainPort;
    }
    catch (error) { }
});
export function makePromiseResult(promiseId, resolvedTypeId, promiseResultTypeId, result, promiseReasonTypeId, handleInsertId) {
    if (typeof handleInsertId === 'number' || typeof handleInsertId === 'string') {
        return {
            from: {
                data: {
                    from_id: promiseId,
                    type_id: resolvedTypeId,
                    to: {
                        data: {
                            type_id: promiseResultTypeId,
                            object: { data: { value: result } },
                        }
                    }
                }
            },
            type_id: promiseReasonTypeId,
            to_id: handleInsertId
        };
    }
    else {
        return {
            from_id: promiseId,
            type_id: resolvedTypeId,
            to: {
                data: {
                    type_id: promiseResultTypeId,
                    object: { data: { value: result } },
                }
            }
        };
    }
}
;
export function processPromises(promises, handleOperationsIds, promiseId, promiseResultTypeId, promiseReasonTypeId, resolvedTypeId, rejectedTypeId, log) {
    return __awaiter(this, void 0, void 0, function* () {
        log("promises.length: ", promises.length);
        yield Promise.allSettled(promises.map((p) => p()))
            .then((values) => __awaiter(this, void 0, void 0, function* () {
            log("values: ", values);
            const promiseResults = [];
            for (let i = 0; i < values.length; i++) {
                const value = values[i];
                const handleInsertId = handleOperationsIds[i];
                let resultTypeId = null;
                let result = null;
                if (value.status == 'fulfilled') {
                    result = value.value;
                    resultTypeId = resolvedTypeId;
                }
                if (value.status == 'rejected') {
                    result = value.reason;
                    resultTypeId = rejectedTypeId;
                }
                log("result: ", result);
                log("resultTypeId: ", resultTypeId);
                promiseResults.push(makePromiseResult(promiseId, resultTypeId, promiseResultTypeId, result, promiseReasonTypeId, handleInsertId));
            }
            try {
                yield deep.insert(promiseResults, { name: 'IMPORT_PROMISES_RESULTS' });
                log("inserted promiseResults: ", JSON.stringify(promiseResults, null, 2));
            }
            catch (e) {
                const serializedError = serializeError(e);
                log('promiseResults insert error: ', JSON.stringify(serializedError, null, 2));
            }
        }));
    });
}
export const containerController = new ContainerController({
    gql_docker_domain: +DOCKER ? 'deep-links' : 'host.docker.internal',
    handlersHash: {}
});
export function getJwt(handlerId, useRunnerDebug) {
    return __awaiter(this, void 0, void 0, function* () {
        const getJwtDebug = Debug('deeplinks:eh:links:getJwt');
        const userTypeId = deep.idLocal('@deep-foundation/core', 'User');
        getJwtDebug("userTypeId: ", JSON.stringify(userTypeId, null, 2));
        const packageTypeId = deep.idLocal('@deep-foundation/core', 'Package');
        getJwtDebug("packageTypeId: ", JSON.stringify(packageTypeId, null, 2));
        const queryString = `query {
    mpUp: mp(where: {
      item_id: {_eq: "${handlerId}"},
      path_item: {type_id: {_in: ["${userTypeId}", "${packageTypeId}"]}}
    }) {
      id
      path_item {
        id
        type_id
        value
      }
      path_item_depth
      position_id
    }
    mpMe: mp(where: {item_id: {_eq: "${handlerId}"}, path_item_id: { _eq: "${handlerId}" }}) {
      id
      path_item_depth
      position_id
    }
  }`;
        const ownerResults = yield client.query({ query: gql `${queryString}` });
        getJwtDebug("ownerResults: ", JSON.stringify(ownerResults, null, 2));
        const mpUp = ownerResults.data.mpUp;
        const mpMe = ownerResults.data.mpMe;
        const possibleOwners = mpMe.map((me) => {
            const getDepthDifference = (depth) => me.path_item_depth - depth;
            const up = mpUp.filter((up) => up.position_id == me.position_id);
            const closestUp = up.sort((a, b) => getDepthDifference(a.path_item_depth) - getDepthDifference(b.path_item_depth))[0];
            return closestUp === null || closestUp === void 0 ? void 0 : closestUp.path_item;
        }).filter(r => !!r);
        getJwtDebug("possibleOwners: ", JSON.stringify(possibleOwners, null, 2));
        const ownerPackage = possibleOwners.find(r => r.type_id == packageTypeId);
        const ownerUser = possibleOwners.find(r => r.type_id == userTypeId);
        let ownerId;
        if (ownerPackage) {
            ownerId = ownerPackage.id;
            getJwtDebug("owner is package");
        }
        else if (ownerUser) {
            ownerId = ownerUser.id;
            getJwtDebug("owner is user");
        }
        else {
            throw new Error("No handler owner found.");
        }
        getJwtDebug("ownerId: ", ownerId);
        const jwt = (yield deep.jwt({ linkId: ownerId })).token;
        useRunnerDebug('jwt', jwt);
        return jwt;
    });
}
export const useRunner = ({ code, isolationProviderImageName, handlerId, data, }) => __awaiter(void 0, void 0, void 0, function* () {
    const useRunnerDebug = Debug('deeplinks:eh:links:useRunner');
    useRunnerDebug("handler4: ");
    const jwt = yield getJwt(handlerId, useRunnerDebug);
    const secret = process.env.DEEPLINKS_HASURA_SECRET;
    const container = yield containerController.newContainer({ publish: +DOCKER ? false : true, forceRestart: true, handler: isolationProviderImageName });
    useRunnerDebug('newContainerResult', container);
    const initResult = yield containerController.initHandler(container);
    useRunnerDebug('initResult', initResult);
    const callResult = yield containerController.callHandler({ code, container, jwt, secret, data });
    useRunnerDebug('callResult', callResult);
    return callResult;
});
export const handlerOperations = {
    Insert: 'HandleInsert',
    Update: 'HandleUpdate',
    Delete: 'HandleDelete',
};
export function handleOperation(operation, triggeredByLinkId, oldLink, newLink, valuesOperation) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const handleOperationDebug = Debug('deeplinks:eh:links:handleOperation');
        handleOperationDebug('handleOperation', operation);
        const handleOperationTypeId = deep.idLocal('@deep-foundation/core', handlerOperations[operation]);
        handleOperationDebug('handleOperationTypeId', handleOperationTypeId);
        const promiseLinksQueryString = `query SELECT_PROMISE_LINKS { 
    promise_links(where: {
      ${!!(oldLink === null || oldLink === void 0 ? void 0 : oldLink.id) ? `old_link_id: { _eq: ${oldLink === null || oldLink === void 0 ? void 0 : oldLink.id} }` : "old_link_id: { _is_null: true }"},
      ${!!(newLink === null || newLink === void 0 ? void 0 : newLink.id) ? `new_link_id: { _eq: ${newLink === null || newLink === void 0 ? void 0 : newLink.id} }` : "new_link_id: { _is_null: true }"},
      ${typeof (valuesOperation) !== 'undefined' ? `values_operation: { _eq: "${valuesOperation}" }` : "values_operation: { _is_null: true }"},
      handle_operation_type_id: { _eq: ${handleOperationTypeId} },
      selector_id: { _is_null: true }
    }) {
      id
      promise_id
      handle_operation_id
      handler_id
      isolation_provider_image_name
      code
    }
  }`;
        handleOperationDebug('promiseLinksQueryString', promiseLinksQueryString);
        const promiseLinksQuery = gql `${promiseLinksQueryString}`;
        const promiseLinksResult = yield client.query({ query: promiseLinksQuery });
        const promiseLinks = (_a = promiseLinksResult === null || promiseLinksResult === void 0 ? void 0 : promiseLinksResult.data) === null || _a === void 0 ? void 0 : _a.promise_links;
        handleOperationDebug("promiseLinks", JSON.stringify(promiseLinks, null, 2));
        handleOperationDebug("promiseLinks?.length", promiseLinks === null || promiseLinks === void 0 ? void 0 : promiseLinks.length);
        if (!(promiseLinks === null || promiseLinks === void 0 ? void 0 : promiseLinks.length)) {
            return;
        }
        const resolvedTypeId = deep.idLocal('@deep-foundation/core', 'Resolved');
        const rejectedTypeId = deep.idLocal('@deep-foundation/core', 'Rejected');
        const promiseResultTypeId = deep.idLocal('@deep-foundation/core', 'PromiseResult');
        const promiseReasonTypeId = deep.idLocal('@deep-foundation/core', 'PromiseReason');
        for (const promiseLink of promiseLinks) {
            const code = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.code;
            const isolationProviderImageName = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.isolation_provider_image_name;
            const handlerId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.handler_id;
            const handleOperationId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.handle_operation_id;
            const promiseId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.promise_id;
            const promises = [];
            const handleOperationsIds = [];
            if (code && isolationProviderImageName && handlerId && handleOperationId) {
                try {
                    promises.push(() => __awaiter(this, void 0, void 0, function* () { return useRunner({ code, handlerId, isolationProviderImageName, data: { triggeredByLinkId, oldLink, newLink, promiseId: promiseId, deeplinksUrl } }); }));
                    handleOperationsIds.push(handleOperationId);
                }
                catch (error) {
                    const serializedError = serializeError(error);
                    handleOperationDebug('error', JSON.stringify(serializedError, null, 2));
                }
            }
            else {
                promises.push(() => __awaiter(this, void 0, void 0, function* () { return Promise.reject(new Error('Id, operation id, code, or image of a handler are not loaded.')); }));
                handleOperationsIds.push(handleOperationId);
            }
            yield processPromises(promises, handleOperationsIds, promiseId, promiseResultTypeId, promiseReasonTypeId, resolvedTypeId, rejectedTypeId, handleOperationDebug);
            yield deep.delete(promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.id, { name: 'DELETE_PROMISE_LINK', table: 'promise_links' });
        }
    });
}
export function handleSelectorOperation(operation, triggeredByLinkId, oldLink, newLink, valuesOperation) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const handleSelectorDebug = debug.extend('handleSelector').extend('log');
        handleSelectorDebug('handleOperation', operation);
        const handleOperationTypeId = deep.idLocal('@deep-foundation/core', handlerOperations[operation]);
        handleSelectorDebug('handleOperationTypeId', handleOperationTypeId);
        const promiseLinksQueryString = `query SELECT_PROMISE_LINKS { 
    promise_links(where: {
      ${!!(oldLink === null || oldLink === void 0 ? void 0 : oldLink.id) ? `old_link_id: { _eq: ${oldLink === null || oldLink === void 0 ? void 0 : oldLink.id} }` : "old_link_id: { _is_null: true }"},
      ${!!(newLink === null || newLink === void 0 ? void 0 : newLink.id) ? `new_link_id: { _eq: ${newLink === null || newLink === void 0 ? void 0 : newLink.id} }` : "new_link_id: { _is_null: true }"},
      ${operation == "Update" ? `values_operation: { _eq: "${valuesOperation}" }` : "values_operation: { _is_null: true }"},
      handle_operation_type_id: { _eq: ${handleOperationTypeId} },
      selector_id: { _is_null: false }
    }) {
      id
      promise_id
      selector_id
      handle_operation_id
      handler_id
      isolation_provider_image_name
      code
    }
  }`;
        handleSelectorDebug('promiseLinksQueryString', promiseLinksQueryString);
        const promiseLinksQuery = gql `${promiseLinksQueryString}`;
        const promiseLinksResult = yield client.query({ query: promiseLinksQuery });
        const promiseLinks = (_a = promiseLinksResult === null || promiseLinksResult === void 0 ? void 0 : promiseLinksResult.data) === null || _a === void 0 ? void 0 : _a.promise_links;
        handleSelectorDebug('promiseLinks', JSON.stringify(promiseLinks, null, 2));
        handleSelectorDebug('promiseLinks.length', promiseLinks === null || promiseLinks === void 0 ? void 0 : promiseLinks.length);
        if (!(promiseLinks === null || promiseLinks === void 0 ? void 0 : promiseLinks.length)) {
            return;
        }
        const resolvedTypeId = deep.idLocal('@deep-foundation/core', 'Resolved');
        const rejectedTypeId = deep.idLocal('@deep-foundation/core', 'Rejected');
        const promiseResultTypeId = deep.idLocal('@deep-foundation/core', 'PromiseResult');
        const promiseReasonTypeId = deep.idLocal('@deep-foundation/core', 'PromiseReason');
        for (const promiseLink of promiseLinks) {
            const code = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.code;
            const isolationProviderImageName = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.isolation_provider_image_name;
            const handlerId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.handler_id;
            const handleOperationId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.handle_operation_id;
            const selectorId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.selector_id;
            const promiseId = promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.promise_id;
            const promises = [];
            const handleOperationsIds = [];
            if (code && isolationProviderImageName && handlerId && handleOperationId) {
                try {
                    promises.push(() => __awaiter(this, void 0, void 0, function* () { return useRunner({ code, handlerId, isolationProviderImageName, data: { triggeredByLinkId, oldLink, newLink, promiseId, selectorId, deeplinksUrl } }); }));
                    handleOperationsIds.push(handleOperationId);
                }
                catch (error) {
                    const serializedError = serializeError(error);
                    handleSelectorDebug('error', JSON.stringify(serializedError, null, 2));
                }
            }
            else {
                promises.push(() => __awaiter(this, void 0, void 0, function* () { return Promise.reject(new Error('Id, operation id, code, or image of a handler are not loaded.')); }));
                handleOperationsIds.push(handleOperationId);
            }
            yield processPromises(promises, handleOperationsIds, promiseId, promiseResultTypeId, promiseReasonTypeId, resolvedTypeId, rejectedTypeId, handleSelectorDebug);
            yield deep.delete(promiseLink === null || promiseLink === void 0 ? void 0 : promiseLink.id, { name: 'DELETE_PROMISE_LINK', table: 'promise_links' });
        }
    });
}
export function handleSchedule(handleScheduleLink, operation) {
    var _a, _b, _c, _d;
    return __awaiter(this, void 0, void 0, function* () {
        const handleScheduleDebug = Debug('deeplinks:eh:links:handleSchedule');
        handleScheduleDebug('handleScheduleLink', handleScheduleLink);
        handleScheduleDebug('operation', operation);
        if (operation == 'INSERT') {
            const schedule = yield deep.select({
                type_id: deep.idLocal('@deep-foundation/core', 'Schedule'),
                out: {
                    id: { _eq: handleScheduleLink.id },
                },
            }, {
                table: 'links',
                returning: 'id value',
            });
            handleScheduleDebug(schedule);
            const scheduleId = (_b = (_a = schedule === null || schedule === void 0 ? void 0 : schedule.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.id;
            const scheduleValue = (_d = (_c = schedule === null || schedule === void 0 ? void 0 : schedule.data) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.value.value;
            handleScheduleDebug('scheduleId', scheduleId);
            handleScheduleDebug('scheduleValue', scheduleValue);
            yield api.query({
                type: 'create_cron_trigger',
                args: {
                    name: `handle_schedule_${handleScheduleLink === null || handleScheduleLink === void 0 ? void 0 : handleScheduleLink.id}`,
                    webhook: `${DOCKER_DEEPLINKS_URL}/api/scheduler`,
                    schedule: scheduleValue,
                    include_in_metadata: true,
                    payload: {
                        scheduleId,
                        schedule: scheduleValue,
                        handleScheduleLinkId: handleScheduleLink === null || handleScheduleLink === void 0 ? void 0 : handleScheduleLink.id,
                    },
                    retry_conf: {
                        num_retries: 3,
                        timeout_seconds: 120,
                        tolerance_seconds: 21675,
                        retry_interval_seconds: 12
                    },
                    comment: `Event trigger for handle schedule link ${handleScheduleLink === null || handleScheduleLink === void 0 ? void 0 : handleScheduleLink.id} with cron schedule definition ${scheduleValue} of ${scheduleId} schedule.`,
                }
            });
            handleScheduleDebug('cron trigger created');
        }
        else if (operation == 'DELETE') {
            yield api.query({
                type: 'delete_cron_trigger',
                args: {
                    name: `handle_schedule_${handleScheduleLink === null || handleScheduleLink === void 0 ? void 0 : handleScheduleLink.id}`,
                }
            });
            handleScheduleDebug('cron trigger deleted');
        }
    });
}
export function handleGql(handleGqlLink, operation) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
    return __awaiter(this, void 0, void 0, function* () {
        const handleGqlDebug = Debug('deeplinks:eh:links:handleGql');
        handleGqlDebug('handleGqlLink', handleGqlLink);
        handleGqlDebug('operation', operation);
        if (operation == 'INSERT') {
            const portTypeId = deep.idLocal('@deep-foundation/core', 'Port');
            const routerStringUseTypeId = deep.idLocal('@deep-foundation/core', 'RouterStringUse');
            const routerListeningTypeId = deep.idLocal('@deep-foundation/core', 'RouterListening');
            const handleRouteTypeId = deep.idLocal('@deep-foundation/core', 'HandleRoute');
            const mainPort = yield getMainPort();
            const routesResult = yield client.query({
                query: gql `
        query {
          ports: links(where: {
            type_id: { _eq: ${portTypeId} },
            in: {
              type_id: { _eq: ${routerListeningTypeId} }
              from: {
                in: {
                  type_id: { _eq: ${routerStringUseTypeId} }
                  from: {
                    out: {
                      type_id: { _eq: ${handleRouteTypeId} },
                      in: {
                        id: { _eq: ${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id} }
                      }
                    }
                  }
                }
              }
            }
          }) {
            id
            port: value
            routerListening: in(where: {
              type_id: { _eq: ${routerListeningTypeId} }
            }) {
              id
              router: from {
                id
                routerStringUse: in(where: {
                  type_id: { _eq: ${routerStringUseTypeId} }
                }) {
                  id
                  routeString: value
                  route: from {
                    id
                    handleRoute: out(where: {
                      type_id: { _eq: ${handleRouteTypeId} },
                      in: {
                        id: { _eq: ${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id} }
                      }
                    }) {
                      id
                      handleGql: in(where: {
                        id: { _eq: ${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id} }
                      }) {
                        id
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `, variables: {}
            });
            const portsResult = routesResult.data.ports;
            handleGqlDebug('portsResult', JSON.stringify(portsResult, null, 2));
            const urls = {};
            for (const port of portsResult) {
                const portValue = (port === null || port === void 0 ? void 0 : port.id) === mainPort ? PORT : (_a = port === null || port === void 0 ? void 0 : port.port) === null || _a === void 0 ? void 0 : _a.value;
                const baseUrl = `http://${DEEPLINKS_ROUTE_HANDLERS_HOST}:${portValue}`;
                for (const routerListening of port === null || port === void 0 ? void 0 : port.routerListening) {
                    for (const routerStringUse of (_b = routerListening === null || routerListening === void 0 ? void 0 : routerListening.router) === null || _b === void 0 ? void 0 : _b.routerStringUse) {
                        const url = `${baseUrl}${(_c = routerStringUse === null || routerStringUse === void 0 ? void 0 : routerStringUse.routeString) === null || _c === void 0 ? void 0 : _c.value}`;
                        if ((_f = (_e = (_d = routerStringUse === null || routerStringUse === void 0 ? void 0 : routerStringUse.route) === null || _d === void 0 ? void 0 : _d.handleRoute) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.id)
                            urls[url] = (_j = (_h = (_g = routerStringUse === null || routerStringUse === void 0 ? void 0 : routerStringUse.route) === null || _g === void 0 ? void 0 : _g.handleRoute) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.id;
                    }
                }
            }
            handleGqlDebug('urls', JSON.stringify(urls, null, 2));
            for (const url of Object.keys(urls)) {
                const handleRouteId = urls[url];
                const remote_schema_name = `handle_gql_handler_${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id}`;
                const options = {
                    type: 'add_remote_schema',
                    args: {
                        name: remote_schema_name,
                        definition: {
                            url,
                            headers: [{ name: 'x-hasura-client', value: 'deeplinks-gql-handler' }],
                            forward_client_headers: true,
                            timeout_seconds: 60
                        }
                    }
                };
                handleGqlDebug('options', JSON.stringify(options, null, 2));
                const handler_path = `${url.replace(DEEPLINKS_ROUTE_HANDLERS_HOST, "localhost").replace('http://', '')}`;
                const waitOnUrl = `${url.replace(DEEPLINKS_ROUTE_HANDLERS_HOST, "localhost").replace('http://', 'http-get://')}?query=%7B__typename%7D`;
                handleGqlDebug('waitOnUrl', waitOnUrl);
                yield waitOn({ resources: [waitOnUrl] });
                try {
                    const response = yield api.query(options);
                    handleGqlDebug('remote schema addition response', JSON.stringify(JSON.parse(toJSON(response)), null, 2));
                    if (response.data.error) {
                        throw response;
                    }
                    handleGqlDebug('remote schema is added');
                    const remote_schema_client = generateApolloClient({
                        secret: process.env.DEEPLINKS_HASURA_SECRET,
                        path: handler_path,
                    });
                    const __schema = yield remote_schema_client.query({ query: gql `${getIntrospectionQuery()}` });
                    const _schema = buildClientSchema(__schema.data);
                    const schema = printSchema(_schema);
                    handleGqlDebug(`remote schema loaded
${schema}`);
                    yield api.query({
                        type: "add_remote_schema_permissions",
                        args: {
                            remote_schema: remote_schema_name,
                            role: "link",
                            definition: { schema },
                            comment: "remote schema permissions for role: link"
                        }
                    }, { route: '/v1/metadata' });
                    handleGqlDebug('remote schema permission for "link" added');
                    yield api.query({
                        type: "add_remote_schema_permissions",
                        args: {
                            remote_schema: remote_schema_name,
                            role: "undefined",
                            definition: { schema },
                            comment: "remote schema permissions for role: undefined"
                        }
                    }, { route: '/v1/metadata' });
                    handleGqlDebug('remote schema permission for "undefined" added');
                }
                catch (rejected) {
                    const processedRejection = serializeError(rejected);
                    handleGqlDebug('rejected', JSON.stringify(processedRejection, null, 2));
                    const handlingErrorTypeId = deep.idLocal('@deep-foundation/core', 'HandlingError');
                    handleGqlDebug('handlingErrorTypeId', handlingErrorTypeId);
                    const error = ((_k = processedRejection === null || processedRejection === void 0 ? void 0 : processedRejection.data) === null || _k === void 0 ? void 0 : _k.error) || '';
                    if (error.includes('remote schema with name') && error.includes('already exists'))
                        return;
                    const insertResult = yield deep.insert({
                        type_id: handlingErrorTypeId,
                        object: { data: { value: processedRejection } },
                        out: { data: [
                                {
                                    type_id: deep.idLocal('@deep-foundation/core', 'HandlingErrorReason'),
                                    to_id: handleRouteId,
                                },
                            ] },
                    }, {
                        name: 'INSERT_HANDLING_ERROR',
                    });
                    handleGqlDebug('remote schema addition error is inserted');
                }
            }
        }
        else if (operation == 'DELETE') {
            const reasonResult = yield client.query({
                query: gql `
        query {
          handleGql: links(where: {
            id: { _eq: ${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id} }
          }) {
            id
          }
          handleRoute: links(where: {
            id: { _eq: ${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.to_id} }
          }) {
            id
          }
        }
      `, variables: {}
            });
            const data = reasonResult.data;
            const reasonId = (_o = (_m = (_l = data === null || data === void 0 ? void 0 : data.handleGql) === null || _l === void 0 ? void 0 : _l[0]) === null || _m === void 0 ? void 0 : _m.id) !== null && _o !== void 0 ? _o : (_q = (_p = data === null || data === void 0 ? void 0 : data.handleRoute) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.id;
            handleGqlDebug('reasonId', reasonId);
            try {
                const response = yield api.query({
                    type: 'remove_remote_schema',
                    args: {
                        name: `handle_gql_handler_${handleGqlLink === null || handleGqlLink === void 0 ? void 0 : handleGqlLink.id}`,
                    },
                });
                handleGqlDebug('remote schema removal response', JSON.stringify(JSON.parse(toJSON(response)), null, 2));
                if (response.data.error) {
                    throw response;
                }
                handleGqlDebug('remote schema is removed');
            }
            catch (rejected) {
                const processedRejection = serializeError(rejected);
                handleGqlDebug('rejected', JSON.stringify(processedRejection, null, 2));
                const handlingErrorTypeId = deep.idLocal('@deep-foundation/core', 'HandlingError');
                handleGqlDebug('handlingErrorTypeId', handlingErrorTypeId);
                const error = ((_r = processedRejection === null || processedRejection === void 0 ? void 0 : processedRejection.data) === null || _r === void 0 ? void 0 : _r.error) || '';
                if (error.includes('remote schema with name') && error.includes('already exists'))
                    return;
                const insertResult = yield deep.insert({
                    type_id: handlingErrorTypeId,
                    object: { data: { value: processedRejection } },
                    out: { data: [
                            {
                                type_id: deep.idLocal('@deep-foundation/core', 'HandlingErrorReason'),
                                to_id: reasonId,
                            },
                        ] },
                }, {
                    name: 'INSERT_HANDLING_ERROR',
                });
                handleGqlDebug('remote schema removal error is inserted');
            }
        }
    });
}
export function handlePort(handlePortLink, operation) {
    var _a, _b, _c, _d, _e, _f;
    return __awaiter(this, void 0, void 0, function* () {
        const handlePortDebug = Debug('deeplinks:eh:links:handlePort');
        handlePortDebug('handlePortLink', handlePortLink);
        handlePortDebug('operation', operation);
        const port = yield deep.select({
            id: { _eq: handlePortLink.from_id },
        }, {
            table: 'links',
            returning: 'id value',
        });
        handlePortDebug(port);
        const portId = (_b = (_a = port === null || port === void 0 ? void 0 : port.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.id;
        const portValue = (_d = (_c = port === null || port === void 0 ? void 0 : port.data) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.value.value;
        handlePortDebug('portId', portId);
        handlePortDebug('portValue', portValue);
        if (operation == 'INSERT') {
            const isolationProvider = yield deep.select({
                in: {
                    id: { _eq: handlePortLink.id },
                },
            }, {
                table: 'links',
                returning: 'id value',
            });
            handlePortDebug('INSERT', isolationProvider);
            const dockerImage = (_f = (_e = isolationProvider === null || isolationProvider === void 0 ? void 0 : isolationProvider.data) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.value.value;
            handlePortDebug('INSERT dockerImage', dockerImage);
            const containerName = `deep-handle-port-${portValue}`;
            handlePortDebug('INSERT containerName', containerName);
            const container = yield containerController.newContainer({ publish: true, forcePort: portValue, forceName: containerName, handler: dockerImage });
            handlePortDebug('INSERT newContainer result', container);
            if (container.error)
                return handlePortDebug('portResult.error', container.error);
            handlePortDebug(`INSERT port handler container ${JSON.stringify(container)} created`);
        }
        else if (operation == 'DELETE') {
            const containerName = `deep-handle-port-${portValue}`;
            handlePortDebug('DELETE containerName', containerName);
            const container = yield containerController.findContainer(containerName);
            handlePortDebug('DELETE container', container);
            yield containerController.dropContainer(container);
            handlePortDebug('DELETE port handler container deleted');
        }
    });
}
export default (req, res) => __awaiter(void 0, void 0, void 0, function* () {
    var _a, _b, _c, _d, _e, _f, _g;
    try {
        let handlePortId;
        try {
            handlePortId = deep.idLocal('@deep-foundation/core', 'HandlePort');
        }
        catch (_h) {
            return res.status(500).json({ error: '@deep-foundation/core package is not ready to support links handlers.' });
        }
        const event = (_a = req === null || req === void 0 ? void 0 : req.body) === null || _a === void 0 ? void 0 : _a.event;
        const triggeredByLinkId = parseInt(event.session_variables["x-hasura-user-id"]);
        log('triggeredByLinkId', triggeredByLinkId);
        const operation = event === null || event === void 0 ? void 0 : event.op;
        if (operation === 'INSERT' || operation === 'UPDATE' || operation === 'DELETE') {
            const oldRow = (_b = event === null || event === void 0 ? void 0 : event.data) === null || _b === void 0 ? void 0 : _b.old;
            if (oldRow) {
                const queryResult = yield deep.select({
                    id: { _eq: oldRow.id },
                }, {
                    returning: `value`,
                });
                oldRow.value = (_d = (_c = queryResult.data) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.value;
            }
            const newRow = (_e = event === null || event === void 0 ? void 0 : event.data) === null || _e === void 0 ? void 0 : _e.new;
            if (newRow) {
                const queryResult = yield deep.select({
                    id: { _eq: newRow.id },
                }, {
                    returning: `value`,
                });
                newRow.value = (_g = (_f = queryResult.data) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.value;
            }
            const current = operation === 'DELETE' ? oldRow : newRow;
            log(`Processing ${current.id} link.`);
            log('operation', operation);
            log('current', current);
            try {
                if (operation === 'INSERT') {
                    yield handleOperation('Insert', triggeredByLinkId, oldRow, newRow);
                    yield handleSelectorOperation('Insert', triggeredByLinkId, oldRow, newRow);
                }
                else if (operation === 'DELETE') {
                    yield handleOperation('Delete', triggeredByLinkId, oldRow, newRow);
                    yield handleSelectorOperation('Delete', triggeredByLinkId, oldRow, newRow);
                }
                else if (operation === 'UPDATE') {
                    yield handleOperation('Update', triggeredByLinkId, oldRow, newRow);
                    yield handleSelectorOperation('Update', triggeredByLinkId, oldRow, newRow);
                }
                const typeId = current.type_id;
                const handleScheduleId = deep.idLocal('@deep-foundation/core', 'HandleSchedule');
                if (typeId === handleScheduleId && (operation === 'INSERT' || operation === 'DELETE')) {
                    yield handleSchedule(current, operation);
                }
                const handleGqlTypeId = deep.idLocal('@deep-foundation/core', 'HandleGql');
                if (typeId === handleGqlTypeId && (operation === 'INSERT' || operation === 'DELETE')) {
                    yield handleGql(current, operation);
                }
                if (typeId === handlePortId && (operation === 'INSERT' || operation === 'DELETE')) {
                    yield handlePort(current, operation);
                }
                log(`Link ${current.id} is proccessed.`);
                if (operation === 'INSERT' && !DENIED_IDS.includes(current.type_id) && ALLOWED_IDS.includes(current.type_id)) {
                    log('resolve', current.id);
                    yield resolve({
                        id: current.id, client,
                        Then: deep.idLocal('@deep-foundation/core', 'Then'),
                        Promise: deep.idLocal('@deep-foundation/core', 'Promise'),
                        Resolved: deep.idLocal('@deep-foundation/core', 'Resolved'),
                        Rejected: deep.idLocal('@deep-foundation/core', 'Rejected'),
                        Results: false,
                    });
                }
                return res.status(200).json({});
            }
            catch (e) {
                const serializedError = serializeError(e);
                error('error', JSON.stringify(serializedError, null, 2));
                if (operation === 'INSERT' && !DENIED_IDS.includes(current.type_id) && ALLOWED_IDS.includes(current.type_id)) {
                    log('reject', current.id);
                    yield reject({
                        id: current.id, client,
                        Then: deep.idLocal('@deep-foundation/core', 'Then'),
                        Promise: deep.idLocal('@deep-foundation/core', 'Promise'),
                        Resolved: deep.idLocal('@deep-foundation/core', 'Resolved'),
                        Rejected: deep.idLocal('@deep-foundation/core', 'Rejected'),
                        Results: false,
                    });
                }
            }
            return res.status(500).json({ error: 'notexplained' });
        }
        return res.status(500).json({ error: 'operation can be only INSERT or UPDATE' });
    }
    catch (e) {
        const serializedError = serializeError(e);
        return res.status(500).json({ error: serializedError });
    }
});
export const handleGqlLinks = () => __awaiter(void 0, void 0, void 0, function* () {
    const handleGqlLinksDebug = Debug('deeplinks:eh:links:handleGqlLinks');
    try {
        const HandleGql = deep.idLocal('@deep-foundation/core', 'HandleGql');
        const { data: handles } = yield deep.select({
            type_id: HandleGql
        });
        for (let h in handles) {
            yield handleGql(handles[h], 'INSERT');
        }
    }
    catch (e) {
        const serializedError = serializeError(e);
        handleGqlLinksDebug('IGNORED ERROR (handleGqlLinks):', JSON.stringify(serializedError, null, 2));
    }
});
const startGqlHandling = () => __awaiter(void 0, void 0, void 0, function* () {
    setInterval(handleGqlLinks, 5000);
});
startGqlHandling();
//# sourceMappingURL=links.js.map