"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 });
// this must be imported to allow async-functions within an AWS lambda environment
// see: https://github.com/babel/babel/issues/5085
require("@babel/polyfill");
const express_1 = __importDefault(require("express"));
const serverless_http_1 = __importDefault(require("serverless-http"));
const attach_storage_1 = require("../components/attach-storage");
const storage_libs_1 = require("../storage/storage-libs");
const libs_1 = require("../libs");
const storage_component_1 = require("../storage/storage-component");
const attach_service_1 = require("../components/attach-service");
const service_libs_1 = require("./service-libs");
const types_1 = __importDefault(require("../types"));
const loader_1 = require("../libs/loader");
// DataLayer imports....
const connect_sequence_1 = __importDefault(require("connect-sequence"));
const graphql_1 = require("graphql");
const attach_data_layer_1 = require("./attach-data-layer");
//import { getClientFilename } from '../libs/server-libs';
//import {loadIsoConfigFromComponent, applyCustomComponents} from "../isolib";
//import { applyAppClientModules } from '../types/client-app-config';
const createServer = (serviceOrientedId, isOffline) => {
    // express is the web-framework that lets us configure the endpoints
    const app = express_1.default();
    // in production, API-Gateway proxy redirects the request to S3
    // serve static files - the async components of the server - only used of localhost
    //app.use('/'+assetsDir, express.static(resolvedAssetsPath));
    // load the IsomorphicComponent
    // we must load it directly from the module here, to enable the aliad of the config_file_path
    const soaConfig = loader_1.loadConfigurationFromModule(require('__CONFIG_FILE_PATH__'), loader_1.INFRASTRUCTURE_MODES.RUNTIME);
    // let's extract it from the root configuration
    const soaApp = loader_1.extractObject(soaConfig, types_1.default.INFRASTRUCTURE_TYPE_CONFIGURATION, serviceOrientedId);
    // let's extract it from the root configuration
    const dataLayer = loader_1.extractObject(soaConfig, types_1.default.INFRASTRUCTURE_TYPE_COMPONENT, __DATALAYER_ID__);
    if (dataLayer) {
        //console.log ("Datalayer Active: ", dataLayer.id)
        console.log("isOffline: ", isOffline);
        if (isOffline) {
            console.log("setOffline!");
            dataLayer.setOffline(true);
        }
        else {
            const cors = require('cors');
            const corsOptions = {
                origin(origin, callback) {
                    callback(null, true);
                },
                credentials: true
            };
            app.use(cors(corsOptions));
            // TODO only allow the domains of the app (S3, APIs)
            var allowCrossDomain = function (req, res, next) {
                res.header('Access-Control-Allow-Origin', '*');
                res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
                //res.header('Access-Control-Allow-Headers', 'Content-Type,token');
                next();
            };
            app.use(allowCrossDomain);
        }
        app.use('/query', (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
            //console.log("request: ", req);
            const parseBody = (body) => {
                try {
                    return JSON.parse(body);
                }
                catch (e) {
                    console.log("cannot parse body: ", body.toString());
                    return body.toJSON();
                }
            };
            const parsedBody = parseBody(req.body);
            //console.log("parsedBody: ", parsedBody);
            if (!parsedBody.query) {
                res.status(500).set({
                    "Access-Control-Allow-Origin": "*",
                }).send(JSON.stringify(req));
            }
            //console.log("now starting qgl-query/mutation")
            yield graphql_1.graphql(dataLayer.getSchema(false), parsedBody.query).then(result_type => {
                //console.log("result_type: ", result_type);
                const entryQueryName = Object.keys(result_type.data)[0];
                // when the query resolves, we get back
                //console.log("pre-resolve | found entry: ", entryQueryName)
                new connect_sequence_1.default(req, res, next)
                    .append(...dataLayer.entries.filter(entry => entry.providesQuery(entryQueryName)).map(entry => entry.middleware.callback))
                    .append((req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
                    //console.log("DL-mw: req: ");
                    //const parsedBody = JSON.parse(req.body);
                    //console.log("parsedBody: ", parsedBody);
                    // now we let the schema resolve with data
                    yield graphql_1.graphql(dataLayer.getSchema(true), parsedBody.query).then(result => res.status(200).set({
                        "Access-Control-Allow-Origin": "*",
                    }).send(JSON.stringify(result)), err => res.set({
                        "Access-Control-Allow-Origin": "*",
                    }).status(500).send(err));
                }))
                    .run();
            }, err => res.set({
                "Access-Control-Allow-Origin": "*",
            }).status(500).send(err));
        }));
    }
    ;
    // let's extract it from the root configuration
    const storages = libs_1.findComponentRecursively(soaConfig, c => storage_component_1.isStorage(c));
    const reqListFiles = (storageId, prefix, listMode, data, onComplete, onError) => {
        //console.log("listFiles function: ", storageId);
        //console.log("found storages: ", storages);
        const storage = storages.find(storage => storage.id == storageId);
        if (storage) {
            storage_libs_1.listFiles(storageId, prefix, listMode, data, onComplete, onError, storage, isOffline);
        }
        else {
            onError("could not find storage with id " + storageId);
        }
    };
    const reqCallService = (id, args, onResult, onError, config, isOffline = false) => service_libs_1.callService(id, args, onResult, onError, soaConfig, isOffline);
    // flattens the callbacks
    const unpackMiddlewares = (middlewares) => {
        // always returns the list of callbacks
        const cbList = (mw) => Array.isArray(mw.callback) ? mw.callback : [mw.callback];
        return middlewares.reduce((res, mw) => res.concat(...cbList(mw)), 
        // attach the callService-function
        [attach_service_1.serviceAttachService(reqCallService)].concat(
        // when we have a dataLayer, let's attach it to the request
        dataLayer ? [attach_data_layer_1.serviceAttachDataLayer(dataLayer)] : [], 
        //when we have a storage, attach the listFiles-function
        storages ? [attach_storage_1.serviceAttachStorage(reqListFiles)] : []));
    };
    // split the clientApps here and define a function for each of the clientApps, with the right middleware
    soaApp.services.map(service => {
        //console.log("found service: ", service);
        if (service.method.toUpperCase() == "GET") {
            app.get(service.path, ...unpackMiddlewares(service.middlewares));
        }
        else if (service.method.toUpperCase() == "POST") {
            app.post(service.path, ...unpackMiddlewares(service.middlewares));
        }
        else if (service.method.toUpperCase() == "PUT") {
            app.put(service.path, ...unpackMiddlewares(service.middlewares));
        }
        else if (service.method.toUpperCase() == "DELETE") {
            app.delete(service.path, ...unpackMiddlewares(service.middlewares));
        }
        return service;
    });
    return app;
};
// these variables are replaced during compilation
exports.default = serverless_http_1.default(createServer(__SERVICEORIENTED_ID__, __ISOFFLINE__));
//# sourceMappingURL=soa-server.js.map