<commit_before>// Copyright 2010-2013 RethinkDB, all rights reserved.
#include <string>

#include "errors.hpp"
#include <boost/algorithm/string/predicate.hpp>

#include "http/http.hpp"
#include "clustering/administration/http/json_adapters.hpp"
#include "clustering/administration/http/semilattice_app.hpp"
#include "clustering/administration/suggester.hpp"
#include "stl_utils.hpp"

template <class metadata_t>
semilattice_http_app_t<metadata_t>::semilattice_http_app_t(
        metadata_change_handler_t<metadata_t> *_metadata_change_handler,
        const clone_ptr_t<watchable_t<change_tracking_map_t<peer_id_t, cluster_directory_metadata_t> > > &_directory_metadata,
        uuid_u _us) :
    directory_metadata(_directory_metadata),
    us(_us),
    metadata_change_handler(_metadata_change_handler) {
    // Do nothing
}

template <class metadata_t>
semilattice_http_app_t<metadata_t>::~semilattice_http_app_t() {
    // Do nothing
}

template <class metadata_t>
void semilattice_http_app_t<metadata_t>::get_root(scoped_cJSON_t *json_out) {
    // keep this in sync with handle's behavior for getting the root
    metadata_t metadata = metadata_change_handler->get();
    vclock_ctx_t json_ctx(us);
    json_ctx_adapter_t<metadata_t, vclock_ctx_t> json_adapter(&metadata, json_ctx);
    json_out->reset(json_adapter.render());
}

template <class metadata_t>
http_res_t semilattice_http_app_t<metadata_t>::handle(const http_req_t &req) {
    try {
        metadata_t metadata = metadata_change_handler->get();

        //as we traverse the json sub directories this will keep track of where we are
        vclock_ctx_t json_ctx(us);
        boost::shared_ptr<json_adapter_if_t> json_adapter_head(new json_ctx_adapter_t<metadata_t, vclock_ctx_t>(&metadata, json_ctx));

        http_req_t::resource_t::iterator it = req.resource.begin();

        //Traverse through the subfields until we're done with the url
        while (it != req.resource.end()) {
            json_adapter_if_t::json_adapter_map_t subfields = json_adapter_head->get_subfields();
            if (subfields.find(*it) == subfields.end()) {
                return http_res_t(HTTP_NOT_FOUND); //someone tried to walk off the edge of the world
            }
            json_adapter_head = subfields[*it];
            it++;
        }

        //json_adapter_head now points to the correct part of the metadata time to build a response and be on our way
        switch (req.method) {
            case GET:
            {
                scoped_cJSON_t json_repr(json_adapter_head->render());
                return http_json_res(json_repr.get());
            }
            break;
            case POST:
            {
                // TODO: Get rid of this release mode wrapper, make Michael unhappy.
#ifdef NDEBUG
                if (!verify_content_type(req, "application/json")) {
                    return http_res_t(HTTP_UNSUPPORTED_MEDIA_TYPE);
                }
#endif
                scoped_cJSON_t change(cJSON_Parse(req.body.c_str()));
                if (!change.get()) { //A null value indicates that parsing failed
                    logINF("Json body failed to parse. Here's the data that failed: %s",
                           req.get_sanitized_body().c_str());
                    return http_res_t(HTTP_BAD_REQUEST);
                }

                json_adapter_head->apply(change.get());

                {
                    scoped_cJSON_t absolute_change(change.release());
                    std::vector<std::string> parts(req.resource.begin(), req.resource.end());
                    for (std::vector<std::string>::reverse_iterator jt = parts.rbegin(); jt != parts.rend(); ++jt) {
                        scoped_cJSON_t inner(absolute_change.release());
                        absolute_change.reset(cJSON_CreateObject());
                        absolute_change.AddItemToObject(jt->c_str(), inner.release());
                    }
                    logINF("Applying data %s", absolute_change.PrintUnformatted().c_str());
                }

                // Determine for which namespaces we should prioritize distribution
                // Default: none
                defaulting_map_t<namespace_id_t, bool> prioritize_distr_for_ns(false);
                const boost::optional<std::string> prefer_distribution_param =
                    req.find_query_param("prefer_distribution");
                if (prefer_distribution_param) {
                    if (prefer_distribution_param.get() == "none") {
                    } else if (prefer_distribution_param.get() == "all") {
                        prioritize_distr_for_ns =
                            defaulting_map_t<namespace_id_t, bool>(true);
                    } else if (prefer_distribution_param.get() == "changed_only") {
                        // TODO!
                        prioritize_distr_for_ns =
                            defaulting_map_t<namespace_id_t, bool>(true);
                    }
                }

                metadata_change_callback(&metadata, prioritize_distr_for_ns);
                metadata_change_handler->update(metadata);

                scoped_cJSON_t json_repr(json_adapter_head->render());
                return http_json_res(json_repr.get());
            }
            break;
            case DELETE:
            {
                json_adapter_head->erase();

                logINF("Deleting %s", req.resource.as_string().c_str());

                metadata_change_callback(&metadata,
                                         defaulting_map_t<namespace_id_t, bool>(false));
                metadata_change_handler->update(metadata);
<commit_msg>Extract the affected name spaces in the semilattice app.<commit_after>// Copyright 2010-2013 RethinkDB, all rights reserved.
#include <string>
#include <vector>

#include "errors.hpp"
#include <boost/algorithm/string/predicate.hpp>

#include "http/http.hpp"
#include "clustering/administration/http/json_adapters.hpp"
#include "clustering/administration/http/semilattice_app.hpp"
#include "clustering/administration/suggester.hpp"
#include "stl_utils.hpp"

template <class metadata_t>
semilattice_http_app_t<metadata_t>::semilattice_http_app_t(
        metadata_change_handler_t<metadata_t> *_metadata_change_handler,
        const clone_ptr_t<watchable_t<change_tracking_map_t<peer_id_t, cluster_directory_metadata_t> > > &_directory_metadata,
        uuid_u _us) :
    directory_metadata(_directory_metadata),
    us(_us),
    metadata_change_handler(_metadata_change_handler) {
    // Do nothing
}

template <class metadata_t>
semilattice_http_app_t<metadata_t>::~semilattice_http_app_t() {
    // Do nothing
}

template <class metadata_t>
void semilattice_http_app_t<metadata_t>::get_root(scoped_cJSON_t *json_out) {
    // keep this in sync with handle's behavior for getting the root
    metadata_t metadata = metadata_change_handler->get();
    vclock_ctx_t json_ctx(us);
    json_ctx_adapter_t<metadata_t, vclock_ctx_t> json_adapter(&metadata, json_ctx);
    json_out->reset(json_adapter.render());
}

// Small helper to extract the changed namespace ids from a JSON change request
class collect_namespaces_exc_t {
public:
    collect_namespaces_exc_t(const std::string &_msg) : msg(_msg) { }
    std::string get_msg() const { return msg; }
private:
    std::string msg;
};
std::vector<namespace_id_t> collect_affected_namespaces(const cJSON *change_request)
    THROWS_ONLY(collect_namespaces_exc_t) {

    if (change_request == NULL) {
        throw collect_namespaces_exc_t("Missing change request");
    }
    if (change_request->type != cJSON_Object ) {
        throw collect_namespaces_exc_t("Unhandled change_request type");
    }
    std::vector<namespace_id_t> result;
    const cJSON *entry = change_request->next;
    while (entry) {
        if (entry->string == NULL) throw collect_namespaces_exc_t("Missing field name");
        try {
            const namespace_id_t ns_id = str_to_uuid(std::string(entry->string));
            result.push_back(ns_id);
        } catch (...) {
            throw collect_namespaces_exc_t("Unable to decode UUID: "
                                           + std::string(entry->string));
        }
        entry = entry->next;
    }
    return result;
}

template <class metadata_t>
http_res_t semilattice_http_app_t<metadata_t>::handle(const http_req_t &req) {
    try {
        metadata_t metadata = metadata_change_handler->get();

        //as we traverse the json sub directories this will keep track of where we are
        vclock_ctx_t json_ctx(us);
        boost::shared_ptr<json_adapter_if_t> json_adapter_head(new json_ctx_adapter_t<metadata_t, vclock_ctx_t>(&metadata, json_ctx));

        http_req_t::resource_t::iterator it = req.resource.begin();

        //Traverse through the subfields until we're done with the url
        while (it != req.resource.end()) {
            json_adapter_if_t::json_adapter_map_t subfields = json_adapter_head->get_subfields();
            if (subfields.find(*it) == subfields.end()) {
                return http_res_t(HTTP_NOT_FOUND); //someone tried to walk off the edge of the world
            }
            json_adapter_head = subfields[*it];
            it++;
        }

        //json_adapter_head now points to the correct part of the metadata time to build a response and be on our way
        switch (req.method) {
            case GET:
            {
                scoped_cJSON_t json_repr(json_adapter_head->render());
                return http_json_res(json_repr.get());
            }
            break;
            case POST:
            {
                // TODO: Get rid of this release mode wrapper, make Michael unhappy.
#ifdef NDEBUG
                if (!verify_content_type(req, "application/json")) {
                    return http_res_t(HTTP_UNSUPPORTED_MEDIA_TYPE);
                }
#endif
                scoped_cJSON_t change(cJSON_Parse(req.body.c_str()));
                if (!change.get()) { //A null value indicates that parsing failed
                    logINF("Json body failed to parse. Here's the data that failed: %s",
                           req.get_sanitized_body().c_str());
                    return http_res_t(HTTP_BAD_REQUEST);
                }

                // Determine for which namespaces we should prioritize distribution
                // Default: none
                defaulting_map_t<namespace_id_t, bool> prioritize_distr_for_ns(false);
                const boost::optional<std::string> prefer_distribution_param =
                    req.find_query_param("prefer_distribution");
                if (prefer_distribution_param) {
                    if (prefer_distribution_param.get() == "none") {
                    } else if (prefer_distribution_param.get() == "all") {
                        prioritize_distr_for_ns =
                            defaulting_map_t<namespace_id_t, bool>(true);
                    } else if (prefer_distribution_param.get() == "changed_only") {
                        try {
                            const std::vector<namespace_id_t> changed_ns =
                                collect_affected_namespaces(change.get());
                            for (auto jt = changed_ns.begin();
                                 jt != changed_ns.end();
                                 ++jt) {
                                prioritize_distr_for_ns.set(*jt, true);
                            }
                        } catch (const collect_namespaces_exc_t &e) {
                            logINF("Unable to extract affected namespaces from request: %s",
                                e.get_msg().c_str());
                            return http_res_t(HTTP_BAD_REQUEST);
                        }
                    } else {
                        logINF("Invalid value for prefer_distribution argument: %s",
                           prefer_distribution_param.get().c_str());
                        return http_res_t(HTTP_BAD_REQUEST);
                    }
                }

                json_adapter_head->apply(change.get());

                {
                    scoped_cJSON_t absolute_change(change.release());
                    std::vector<std::string> parts(req.resource.begin(), req.resource.end());
                    for (std::vector<std::string>::reverse_iterator jt = parts.rbegin(); jt != parts.rend(); ++jt) {
                        scoped_cJSON_t inner(absolute_change.release());
                        absolute_change.reset(cJSON_CreateObject());
                        absolute_change.AddItemToObject(jt->c_str(), inner.release());
                    }
                    logINF("Applying data %s", absolute_change.PrintUnformatted().c_str());
                }

                metadata_change_callback(&metadata, prioritize_distr_for_ns);
                metadata_change_handler->update(metadata);

                scoped_cJSON_t json_repr(json_adapter_head->render());
                return http_json_res(json_repr.get());
            }
            break;
            case DELETE:
            {
                json_adapter_head->erase();

                logINF("Deleting %s", req.resource.as_string().c_str());

                metadata_change_callback(&metadata,
                                         defaulting_map_t<namespace_id_t, bool>(false));
                metadata_change_handler->update(metadata);

{'fraction_non_alphanumeric': 0.09669002534665275, 'fraction_numerical': 0.0011927836588638736, 'mean_word_length': 2.160188457008245, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 82, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2270678', 'n_tokens_mistral': 3870, 'n_tokens_neox': 3670, 'n_words': 714}