org.entcore.feeder.dictionary.structures.PostImport.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.feeder.dictionary.structures.PostImport.java

Source

/*
 * Copyright  "Open Digital Education", 2016
 *
 * This program is published by "Open Digital Education".
 * You must indicate the name of the software and the company in any production /contribution
 * using the software and indicate on the home page of the software industry in question,
 * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/.
 *
 * This program is free software, licensed under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, version 3 of the License.
 *
 * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License.
 * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications.
 *
 * You should have received a copy of the GNU Affero General Public License along with the software.
 * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives.
    
 */

package org.entcore.feeder.dictionary.structures;

import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.http.*;
import org.entcore.common.appregistry.ApplicationUtils;
import org.entcore.common.events.EventStore;
import org.entcore.common.events.EventStoreFactory;
import org.entcore.common.user.UserInfos;
import org.entcore.feeder.Feeder;
import org.entcore.common.neo4j.Neo4j;
import org.entcore.feeder.exceptions.TransactionException;
import org.entcore.feeder.utils.TransactionHelper;
import org.entcore.feeder.utils.TransactionManager;
import io.vertx.core.*;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;

import static fr.wseduc.webutils.Utils.handlerToAsyncHandler;

public class PostImport {

    private static final Logger logger = LoggerFactory.getLogger(PostImport.class);
    private final EventBus eb;
    private final EventStore eventStore;
    private final DuplicateUsers duplicateUsers;
    private final Neo4j neo4j = TransactionManager.getNeo4jHelper();
    private final JsonObject config;
    private final Vertx vertx;

    public PostImport(Vertx vertx, DuplicateUsers duplicateUsers, JsonObject config) {
        this.vertx = vertx;
        this.eb = vertx.eventBus();
        eventStore = EventStoreFactory.getFactory().getEventStore(Feeder.class.getSimpleName());
        this.duplicateUsers = duplicateUsers;
        this.config = config;
    }

    public void execute() {
        execute(null);
    }

    public void execute(String source) {
        storeImportedEvent();
        if (source == null || config.getJsonArray("exclude-mark-duplicates-by-source") == null
                || !config.getJsonArray("exclude-mark-duplicates-by-source").contains(source)) {
            duplicateUsers.markDuplicates(new Handler<JsonObject>() {
                @Override
                public void handle(final JsonObject event) {
                    duplicateUsers.autoMergeDuplicatesInStructure(new Handler<AsyncResult<JsonArray>>() {
                        @Override
                        public void handle(AsyncResult<JsonArray> mergedUsers) {
                            applyComRules(getFinalHandler(source));
                        }
                    });
                }
            });
        } else {
            applyComRules(getFinalHandler(source));
        }
    }

    private Handler<Void> getFinalHandler(String source) {
        return new Handler<Void>() {
            @Override
            public void handle(Void v) {
                if (config.getBoolean("notify-apps-after-import", true)) {
                    ApplicationUtils.afterImport(eb);
                }
                if (config.getJsonObject("ws-call-after-import") != null) {
                    wsCall(config.getJsonObject("ws-call-after-import"));
                }
                if (config.getJsonArray("publish-classes-update") != null
                        && config.getJsonArray("publish-classes-update").contains(source)) {
                    publishClassesUpdate();
                }
            }
        };
    }

    private void publishClassesUpdate() {
        final String query = "MATCH (u:User) "
                + "WHERE (has(u.classes) AND not(has(u.oldClasses))) OR u.classes <> u.oldClasses "
                + "RETURN u.id as userId, u.externalId as userExternalId, u.classes as classes, "
                + "u.oldClasses as oldClasses, u.created as created, timestamp() as timestamp";
        final String setOldClasses = "MATCH (u:User) " + "WHERE has(u.classes) " + "SET u.oldClasses = u.classes ";
        try {
            final TransactionHelper tx = TransactionManager.getTransaction();
            final JsonObject params = new JsonObject();
            tx.add(query, params);
            tx.add(setOldClasses, params);
            tx.commit(res -> {
                if ("ok".equals(res.body().getString("status"))) {
                    JsonArray r = res.body().getJsonArray("results");
                    if (r != null && r.getJsonArray(0) != null && r.getJsonArray(0).size() > 0) {
                        eb.publish(Feeder.USER_REPOSITORY, new JsonObject().put("action", "users-classes-update")
                                .put("users-classes-update", r.getJsonArray(0)));
                    }
                } else {
                    logger.error(
                            "Error in publish classes update transaction : " + res.body().getString("message"));
                }
            });
        } catch (TransactionException e) {
            logger.error("Error in publish classes update transaction.", e);
        }
    }

    private void wsCall(JsonObject object) {
        for (String url : object.fieldNames()) {
            final JsonArray endpoints = object.getJsonArray(url);
            if (endpoints == null || endpoints.size() < 1)
                continue;
            try {
                final URI uri = new URI(url);
                HttpClientOptions options = new HttpClientOptions().setDefaultHost(uri.getHost())
                        .setDefaultPort(uri.getPort()).setMaxPoolSize(16).setSsl("https".equals(uri.getScheme()))
                        .setConnectTimeout(10000).setKeepAlive(false);
                final HttpClient client = vertx.createHttpClient(options);

                final Handler[] handlers = new Handler[endpoints.size() + 1];
                handlers[handlers.length - 1] = new Handler<Void>() {
                    @Override
                    public void handle(Void v) {
                        client.close();
                    }
                };
                for (int i = endpoints.size() - 1; i >= 0; i--) {
                    final int ji = i;
                    handlers[i] = new Handler<Void>() {
                        @Override
                        public void handle(Void v) {
                            final JsonObject j = endpoints.getJsonObject(ji);
                            logger.info("endpoint : " + j.encode());
                            final HttpClientRequest req = client.request(HttpMethod.valueOf(j.getString("method")),
                                    j.getString("uri"), new Handler<HttpClientResponse>() {
                                        @Override
                                        public void handle(HttpClientResponse resp) {
                                            if (resp.statusCode() >= 300) {
                                                logger.warn("Endpoint " + j.encode() + " error : "
                                                        + resp.statusCode() + " " + resp.statusMessage());
                                            }
                                            handlers[ji + 1].handle(null);
                                        }
                                    });
                            JsonObject headers = j.getJsonObject("headers");
                            if (headers != null && headers.size() > 0) {
                                for (String h : headers.fieldNames()) {
                                    req.putHeader(h, headers.getString(h));
                                }
                            }
                            req.exceptionHandler(
                                    e -> logger.error("Error in ws call post import : " + j.encode(), e));
                            if (j.getString("body") != null) {
                                req.end(j.getString("body"));
                            } else {
                                req.end();
                            }
                        }
                    };
                }
                handlers[0].handle(null);
            } catch (URISyntaxException e) {
                logger.error("Invalid uri in ws call after import : " + url, e);
            }
        }
    }

    private void storeImportedEvent() {
        String countQuery = "MATCH (:User) WITH count(*) as nbUsers "
                + "MATCH (:Structure) WITH count(*) as nbStructures, nbUsers "
                + "MATCH (:Class) RETURN nbUsers, nbStructures, count(*) as nbClasses ";
        neo4j.execute(countQuery, (JsonObject) null, new Handler<Message<JsonObject>>() {
            @Override
            public void handle(Message<JsonObject> event) {
                JsonArray res = event.body().getJsonArray("result");
                if ("ok".equals(event.body().getString("status")) && res != null && res.size() == 1) {
                    eventStore.createAndStoreEvent(Feeder.FeederEvent.IMPORT.name(), (UserInfos) null,
                            res.getJsonObject(0));
                } else {
                    logger.error(event.body().getString("message"));
                }
            }
        });
    }

    private void applyComRules(final Handler<Void> handler) {
        if (config.getBoolean("apply-communication-rules", false)) {
            String q = "MATCH (s:Structure) return COLLECT(s.id) as ids";
            neo4j.execute(q, new JsonObject(), new Handler<Message<JsonObject>>() {
                @Override
                public void handle(Message<JsonObject> message) {
                    JsonArray ids = message.body().getJsonArray("result",
                            new fr.wseduc.webutils.collections.JsonArray());
                    if ("ok".equals(message.body().getString("status")) && ids != null && ids.size() == 1) {
                        JsonObject j = new JsonObject().put("action", "initAndApplyDefaultCommunicationRules")
                                .put("schoolIds", (ids.getJsonObject(0)).getJsonArray("ids",
                                        new fr.wseduc.webutils.collections.JsonArray()));
                        eb.send("wse.communication", j, new DeliveryOptions().setSendTimeout(3600 * 1000l),
                                handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                                    @Override
                                    public void handle(Message<JsonObject> event) {
                                        if (!"ok".equals(event.body().getString("status"))) {
                                            logger.error("Init rules error : " + event.body().getString("message"));
                                        } else {
                                            logger.info("Communication rules applied.");
                                        }
                                        handler.handle(null);
                                    }
                                }));
                    } else {
                        logger.error(message.body().getString("message"));
                        handler.handle(null);
                    }
                }
            });
        } else {
            handler.handle(null);
        }
    }

}