<commit_before>'use strict';

import colors from 'colors';
import createAdminApi from './adminApi';
import assign from 'object-assign';
import kongState from './kongState';
import {
    noop,
    createApi,
    removeApi,
    updateApi,
    addApiPlugin,
    removeApiPlugin,
    updateApiPlugin,
    createConsumer,
    removeConsumer,
    addConsumerCredentials,
    updateConsumerCredentials,
    removeConsumerCredentials
} from './actions';

export default async function execute(config, adminApi) {
    const actions = [
        ...apis(config.apis),
        ...consumers(config.consumers)
    ];

    return actions
        .map(_bindWorldState(adminApi))
        .reduce((promise, action) => promise.then(_executeActionOnApi(action, adminApi)), Promise.resolve(''));
}

export function apis(apis = []) {
    return apis.reduce((actions, api) => [...actions, _api(api), ..._apiPlugins(api)], []);
};

export function plugins(apiName, plugins) {
    return plugins.reduce((actions, plugin) => [...actions, _plugin(apiName, plugin)], []);
}

export function consumers(consumers = []) {
    return consumers.reduce((calls, consumer) => [...calls, _consumer(consumer), ..._consumerCredentials(consumer)], []);
}

export function credentials(username, credentials) {
    return credentials.reduce((actions, credential) => [...actions, _consumerCredential(username, credential)], []);
}

function _executeActionOnApi(action, adminApi) {
    return async () => {
        const params = await action();

        if (params.noop) {
            return Promise.resolve('No-op');
        }

        return adminApi
            .requestEndpoint(params.endpoint, params)
            .then(response => {
                console.info(
                    `\n${params.method.blue}`,
                    response.ok ? ('' + response.status).bold.green : ('' + response.status).bold.red,
                    adminApi.router(params.endpoint).blue,
                    "\n",
                    params.body ? params.body : ''
                );

                if (!response.ok) {
                    if (params.endpoint.name == 'consumer' && params.method == 'DELETE') {
                        console.log('Bug in Kong throws error, Consumer has still been removed will continue'.bold.green);

                        return response;
                    }

                    return response.text()
                        .then(content => {
                            throw new Error(`${response.statusText}\n${content}`);
                        });
                } else {
                    response.text()
                        .then(content => {
                            console.info(`Response status ${response.statusText}:`.green, "\n", JSON.parse(content));
                        });
                }

                return response;
            });
        }
}

function _bindWorldState(adminApi) {
    return f => async () => {
        const state = await kongState(adminApi);
        return f(_createWorld(state));
    }
}

function _createWorld({apis, consumers}) {
    const world = {
        hasApi: apiName => apis.some(api => api.name === apiName),
        getApi: apiName => {
            const api = apis.find(api => api.name === apiName);

            if (!api) {
                throw new Error(`Unable to find api ${apiName}`);
            }

            return api;
        },
        getPlugin: (apiName, pluginName) => {
            const plugin = world.getApi(apiName).plugins.find(plugin => plugin.name == pluginName);

            if (!plugin) {
                throw new Error(`Unable to find plugin ${pluginName}`);
            }

            return plugin;
        },
        getPluginId: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).id;
        },
        getPluginAttributes: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).config;
        },
        hasPlugin: (apiName, pluginName) => {
            return apis.some(api => api.name === apiName && api.plugins.some(plugin => plugin.name == pluginName));
        },
        hasConsumer: (username) => {
            return consumers.some(consumer => consumer.username === username);
        },
        hasConsumerCredential: (username, name, attributes) => {
            switch(name) {
                case 'oauth2': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.oauth2.some(oa => oa.client_id == attributes.client_id));
                }
                case 'key-auth': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.keyAuth.some(k => k.key == attributes.key));
                }
                case 'jwt': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.jwt.some(k => k.key == attributes.key));
                }
                case 'basic-auth': {
                    return consumers.some(
                            c => c.username === username &&
                            c.credentials.basicAuth.some(k => k.username == attributes.username)
                    );
                }
            }

            throw new Error(`Unknown credential "${name}"`);
        },
        getConsumerCredentialId: (username, name, attributes) => {
            const consumer = consumers.find(c => c.username === username);

            if (!consumer) {
                throw new Error(`Unable to find consumer ${username}`);
            }

            const credential = extractCredentialId(consumer.credentials, name, attributes);

            if (!credential) {
                throw new Error(`Unable to find credential`);
            }

            return credential.id;
        },

        isApiUpToDate: (api) => {
            let current = world.getApi(api.name);

            let different = Object.keys(api.attributes).filter(key => {
                return api.attributes[key] !== current[key];
            });

            return different.length == 0;
        },

        isApiPluginUpToDate: (apiName, plugin) => {
            let current = world.getPluginAttributes(apiName, plugin.name);

            // TODO keys can be in form of "config.somefoo: value"
            let different = Object.keys(plugin.attributes.config).filter(key => {
                return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
            });

            return different.length === 0;
        }
    };

    return world;
}

function extractCredentialId(credentials, name, attributes) {<commit_msg>Fix when api plugin has no attributes<commit_after>'use strict';

import colors from 'colors';
import createAdminApi from './adminApi';
import assign from 'object-assign';
import kongState from './kongState';
import {
    noop,
    createApi,
    removeApi,
    updateApi,
    addApiPlugin,
    removeApiPlugin,
    updateApiPlugin,
    createConsumer,
    removeConsumer,
    addConsumerCredentials,
    updateConsumerCredentials,
    removeConsumerCredentials
} from './actions';

export default async function execute(config, adminApi) {
    const actions = [
        ...apis(config.apis),
        ...consumers(config.consumers)
    ];

    return actions
        .map(_bindWorldState(adminApi))
        .reduce((promise, action) => promise.then(_executeActionOnApi(action, adminApi)), Promise.resolve(''));
}

export function apis(apis = []) {
    return apis.reduce((actions, api) => [...actions, _api(api), ..._apiPlugins(api)], []);
};

export function plugins(apiName, plugins) {
    return plugins.reduce((actions, plugin) => [...actions, _plugin(apiName, plugin)], []);
}

export function consumers(consumers = []) {
    return consumers.reduce((calls, consumer) => [...calls, _consumer(consumer), ..._consumerCredentials(consumer)], []);
}

export function credentials(username, credentials) {
    return credentials.reduce((actions, credential) => [...actions, _consumerCredential(username, credential)], []);
}

function _executeActionOnApi(action, adminApi) {
    return async () => {
        const params = await action();

        if (params.noop) {
            return Promise.resolve('No-op');
        }

        return adminApi
            .requestEndpoint(params.endpoint, params)
            .then(response => {
                console.info(
                    `\n${params.method.blue}`,
                    response.ok ? ('' + response.status).bold.green : ('' + response.status).bold.red,
                    adminApi.router(params.endpoint).blue,
                    "\n",
                    params.body ? params.body : ''
                );

                if (!response.ok) {
                    if (params.endpoint.name == 'consumer' && params.method == 'DELETE') {
                        console.log('Bug in Kong throws error, Consumer has still been removed will continue'.bold.green);

                        return response;
                    }

                    return response.text()
                        .then(content => {
                            throw new Error(`${response.statusText}\n${content}`);
                        });
                } else {
                    response.text()
                        .then(content => {
                            console.info(`Response status ${response.statusText}:`.green, "\n", JSON.parse(content));
                        });
                }

                return response;
            });
        }
}

function _bindWorldState(adminApi) {
    return f => async () => {
        const state = await kongState(adminApi);
        return f(_createWorld(state));
    }
}

function _createWorld({apis, consumers}) {
    const world = {
        hasApi: apiName => apis.some(api => api.name === apiName),
        getApi: apiName => {
            const api = apis.find(api => api.name === apiName);

            if (!api) {
                throw new Error(`Unable to find api ${apiName}`);
            }

            return api;
        },
        getPlugin: (apiName, pluginName) => {
            const plugin = world.getApi(apiName).plugins.find(plugin => plugin.name == pluginName);

            if (!plugin) {
                throw new Error(`Unable to find plugin ${pluginName}`);
            }

            return plugin;
        },
        getPluginId: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).id;
        },
        getPluginAttributes: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).config;
        },
        hasPlugin: (apiName, pluginName) => {
            return apis.some(api => api.name === apiName && api.plugins.some(plugin => plugin.name == pluginName));
        },
        hasConsumer: (username) => {
            return consumers.some(consumer => consumer.username === username);
        },
        hasConsumerCredential: (username, name, attributes) => {
            switch(name) {
                case 'oauth2': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.oauth2.some(oa => oa.client_id == attributes.client_id));
                }
                case 'key-auth': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.keyAuth.some(k => k.key == attributes.key));
                }
                case 'jwt': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.jwt.some(k => k.key == attributes.key));
                }
                case 'basic-auth': {
                    return consumers.some(
                            c => c.username === username &&
                            c.credentials.basicAuth.some(k => k.username == attributes.username)
                    );
                }
            }

            throw new Error(`Unknown credential "${name}"`);
        },
        getConsumerCredentialId: (username, name, attributes) => {
            const consumer = consumers.find(c => c.username === username);

            if (!consumer) {
                throw new Error(`Unable to find consumer ${username}`);
            }

            const credential = extractCredentialId(consumer.credentials, name, attributes);

            if (!credential) {
                throw new Error(`Unable to find credential`);
            }

            return credential.id;
        },

        isApiUpToDate: (api) => {
            let current = world.getApi(api.name);

            let different = Object.keys(api.attributes).filter(key => {
                return api.attributes[key] !== current[key];
            });

            return different.length == 0;
        },

        isApiPluginUpToDate: (apiName, plugin) => {
            let current = world.getPluginAttributes(apiName, plugin.name);

            if (false == plugin.hasOwnProperty('attributes')) {
                // of a plugin has no attributes, and its been added then it is up to date
                return true;
            }

            // TODO keys can be in form of "config.somefoo: value"
            let different = Object.keys(plugin.attributes.config).filter(key => {
                return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
            });

            return different.length === 0;
        }
    };

    return world;
}

function extractCredentialId(credentials, name, attributes) {
{'fraction_non_alphanumeric': 0.12793657720561497, 'fraction_numerical': 0.0005818604989453779, 'mean_word_length': 1.685546875, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2517694', 'n_tokens_mistral': 3528, 'n_tokens_neox': 3381, 'n_words': 761}