All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.nlpcraft.client.impl.NCClientImpl Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.nlpcraft.client.impl;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpEntity;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.nlpcraft.client.NCClient;
import org.apache.nlpcraft.client.NCClientException;
import org.apache.nlpcraft.client.NCCompany;
import org.apache.nlpcraft.client.NCElementSynonymsData;
import org.apache.nlpcraft.client.NCFeedback;
import org.apache.nlpcraft.client.NCModelInfo;
import org.apache.nlpcraft.client.NCNewCompany;
import org.apache.nlpcraft.client.NCProbe;
import org.apache.nlpcraft.client.NCResult;
import org.apache.nlpcraft.client.NCSuggestionData;
import org.apache.nlpcraft.client.NCUser;
import org.apache.nlpcraft.client.impl.beans.NCAskBean;
import org.apache.nlpcraft.client.impl.beans.NCAskSyncBean;
import org.apache.nlpcraft.client.impl.beans.NCCheckBean;
import org.apache.nlpcraft.client.impl.beans.NCCompanyBean;
import org.apache.nlpcraft.client.impl.beans.NCCompanyTokenResetBean;
import org.apache.nlpcraft.client.impl.beans.NCElementSynonymsDataBean;
import org.apache.nlpcraft.client.impl.beans.NCErrorMessageBean;
import org.apache.nlpcraft.client.impl.beans.NCFeedbackAddBean;
import org.apache.nlpcraft.client.impl.beans.NCFeedbackAllBean;
import org.apache.nlpcraft.client.impl.beans.NCModelInfoResultBean;
import org.apache.nlpcraft.client.impl.beans.NCProbesAllBean;
import org.apache.nlpcraft.client.impl.beans.NCRequestStateBean;
import org.apache.nlpcraft.client.impl.beans.NCSigninBean;
import org.apache.nlpcraft.client.impl.beans.NCStatusResponseBean;
import org.apache.nlpcraft.client.impl.beans.NCSuggestionResultBean;
import org.apache.nlpcraft.client.impl.beans.NCTokenCreationBean;
import org.apache.nlpcraft.client.impl.beans.NCUserAddBean;
import org.apache.nlpcraft.client.impl.beans.NCUserBean;
import org.apache.nlpcraft.client.impl.beans.NCUsersAllBean;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Client implementation.
 */
@SuppressWarnings("JavaDoc")
public class NCClientImpl implements NCClient {
    private static final String STATUS_API_OK = "API_OK";

    private static final Type MAP_TYPE = new TypeToken>(){}.getType();

    private static final Gson gsonExt = new Gson();
    private static final Gson gson =
        new GsonBuilder().registerTypeAdapter(
            NCRequestStateBean.class,
            (JsonDeserializer) (e, type, ctx) -> {
                JsonObject o = e.getAsJsonObject();
                NCRequestStateBean b = new NCRequestStateBean();

                b.setSrvReqId(o.get("srvReqId").getAsString());
                b.setTxt(o.get("txt").getAsString());
                b.setUsrId(o.get("usrId").getAsLong());
                b.setMdlId(o.get("mdlId").getAsString());
                b.setProbeId(convert(o, "probeId", JsonElement::getAsString));
                b.setResultType(convert(o, "resType", JsonElement::getAsString));
                b.setResultBody(
                    convert(o, "resBody", (resBody) -> resBody == null ?
                        null :
                        resBody.isJsonObject() ?
                            resBody.getAsJsonObject().toString() :
                            resBody.getAsString())
                );
                b.setResultMeta(convert(o, "resMeta", e1 -> {
                    Map m = gsonExt.fromJson(e1, MAP_TYPE);

                    return m == null || m.isEmpty() ? null : m;
                }));
                b.setStatus(o.get("status").getAsString());
                b.setErrorCode(convert(o, "errorCode", JsonElement::getAsInt));
                b.setError(convert(o, "error", JsonElement::getAsString));
                b.setLogHolder(convert(o, "logHolder", (logHolder) -> logHolder.getAsJsonObject().toString()));

                return b;
            }).create();
    
    private static final Logger log = LogManager.getLogger(NCClientImpl.class);
    private static final String AUTH_ERR = "NC_INVALID_ACCESS_TOKEN";
    
    private Supplier httpCliGen;
    private RequestConfig reqCfg;
    private String baseUrl;
    private String email;
    private String pwd;
    private Boolean cancelOnExit;

    private CloseableHttpClient httpCli;
    private String acsTok;
    private volatile boolean started = false;
    
    private static T convert(JsonObject o, String name, Function converter) {
        JsonElement e = o.get(name);
    
        return e != null ? converter.apply(e) : null;
    }

    @Override
    public String getClientUserEmail() {
        return email;
    }

    @Override
    public String getClientUserPassword() {
        return pwd;
    }

    @Override
    public boolean isClientCancelOnExit() {
        return cancelOnExit;
    }

    @Override
    public String getClientBaseUrl() {
        return baseUrl;
    }

    /**
     *
     * @return
     */
    public Supplier getClientSupplier() {
        return httpCliGen;
    }
    
    /**
     *
     * @param httpCliGen
     */
    public void setClientSupplier(Supplier httpCliGen) {
        this.httpCliGen = httpCliGen;
    }
    
    /**
     *
     * @return
     */
    public RequestConfig getRequestConfig() {
        return reqCfg;
    }
    
    /**
     *
     * @param reqCfg
     */
    public void setRequestConfig(RequestConfig reqCfg) {
        this.reqCfg = reqCfg;
    }
    
    /**
     *
     * @return
     */
    public String getBaseUrl() {
        return baseUrl;
    }
    
    /**
     *
     * @param baseUrl
     */
    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     *
     * @return
     */
    public String getEmail() {
        return email;
    }
    
    /**
     *
     * @param email
     */
    public void setEmail(String email) {
        this.email = email;
    }
    
    /**
     *
     * @return
     */
    public String getPassword() {
        return pwd;
    }
    
    /**
     *
     * @param pwd
     */
    public void setPassword(String pwd) {
        this.pwd = pwd;
    }
    
    /**
     *
     * @return
     */
    public Boolean isCancelOnExit() {
        return cancelOnExit;
    }
    
    /**
     *
     * @param cancelOnExit
     */
    public void setCancelOnExit(Boolean cancelOnExit) {
        this.cancelOnExit = cancelOnExit;
    }
    
    /**
     *
     * @throws IOException
     * @throws NCClientException
     */
    public void initialize() throws IOException, NCClientException {
        httpCli = httpCliGen.get();
    
        if (reqCfg == null)
            reqCfg = RequestConfig.DEFAULT;
    
        acsTok = restSignin();
        
        started = true;
    }
    
    /**
     *
     * @param r
     * @throws NCClientException
     */
    private static void checkStatus(NCStatusResponseBean r) throws NCClientException {
        checkStatus(r.getStatus());
    }
    
    /**
     *
     * @param status
     */
    private static void checkStatus(String status) {
        if (!status.equals(STATUS_API_OK))
            throw new NCClientException(String.format("Unexpected message status: %s", status));
    }
    
    /**
     *
     * @param js
     * @param type
     * @param 
     * @return
     * @throws NCClientException
     */
    private  T checkAndExtract(String js, Type type) throws NCClientException {
        T t = gson.fromJson(js, type);

        checkStatus(t.getStatus());
        
        return t;
    }
    
    /**
     *
     * @param url
     * @param ps
     * @return
     * @throws NCClientException
     * @throws IOException
     * @throws IllegalStateException
     */
    @SafeVarargs
    private String post(String url, Pair... ps) throws NCClientException, IOException {
        if (!started)
            throw new IllegalStateException("Client is not initialized.");
        
        List> psList =
            Arrays.stream(ps).filter(p -> p != null && p.getRight() != null).collect(Collectors.toList());
    
        try {
            return postPlain(url, psList);
        }
        catch (NCClientException e) {
            if (!AUTH_ERR.equals(e.getServerCode()))
                throw e;
            
            try {
                log.debug("Reconnect attempt because token is invalid.");
    
                acsTok = restSignin();
    
                log.debug("Reconnected OK.");
    
                // Replaces token.
                return postPlain(url, psList.stream().
                    map(p -> p.getLeft().equals("acsTok") ? Pair.of("acsTok", acsTok) : p).
                    collect(Collectors.toList()));
            }
            catch (NCClientException e1) {
                throw e;
            }
        }
    }
    
    /**
     *
     * @param url
     * @param ps
     * @return
     * @throws NCClientException
     * @throws IOException
     */
    private String postPlain(String url, List> ps) throws NCClientException, IOException {
        HttpPost post = new HttpPost(baseUrl + url);
        
        try {
            post.setConfig(reqCfg);
    
            StringEntity entity = new StringEntity(
                gson.toJson(ps.stream().collect(Collectors.toMap(Pair::getKey, Pair::getValue))),
                "UTF-8"
            );
            
            post.setHeader("Content-Type", "application/json");
            post.setEntity(entity);
            
            ResponseHandler h = resp -> {
                int code = resp.getStatusLine().getStatusCode();
                
                HttpEntity e = resp.getEntity();
                
                String js = e != null ? EntityUtils.toString(e) : null;
    
                if (js == null)
                    throw new NCClientException(String.format("Unexpected empty response [code=%d]", code));

                if (code == 200)
                    return js;

                NCErrorMessageBean err;
                
                try {
                     err = gson.fromJson(js, NCErrorMessageBean.class);
                }
                catch (Exception e1) {
                    throw new NCClientException(String.format("Unexpected server error [code=%d]", code));
                }

                throw new NCClientException(err.getMessage(), err.getCode());
            };
            
            return httpCli.execute(post, h);
        }
        finally {
            post.releaseConnection();
        }
    }
    
    /**
     * @param v
     * @param name
     * @throws IllegalArgumentException
     */
    private void notNull(String v, String name) throws IllegalArgumentException {
        if (v == null || v.trim().isEmpty())
            throw new IllegalArgumentException(String.format("Parameter cannot be null or empty: '%s'", name));
    }
    
    /**
     *
     * @return
     * @throws IOException
     * @throws NCClientException
     */
    private String restSignin() throws IOException, NCClientException {
        NCSigninBean b =
            checkAndExtract(
                postPlain(
                    "/signin",
                    Arrays.asList(
                        Pair.of("email", email),
                        Pair.of("passwd", pwd)
                    )
                ),
                NCSigninBean.class
            );
        
        return b.getAccessToken();
    }
    
    
    @Override
    public long addUser(
        String email,
        String passwd,
        String firstName,
        String lastName,
        String avatarUrl,
        boolean isAdmin,
        Map properties,
        String extId
    ) throws NCClientException, IOException {
        notNull(email, "email");
        notNull(passwd, "passwd");
        notNull(firstName, "firstName");
        notNull(lastName, "lastName");
    
        NCUserAddBean b =
            checkAndExtract(
                post(
                    "user/add",
                    Pair.of("acsTok", acsTok),
                    Pair.of("email", email),
                    Pair.of("passwd", passwd),
                    Pair.of("firstName", firstName),
                    Pair.of("lastName", lastName),
                    Pair.of("isAdmin", isAdmin),
                    Pair.of("avatarUrl", avatarUrl),
                    Pair.of("properties", properties),
                    Pair.of("extId", extId)
                ),
                NCUserAddBean.class
            );
        
        return b.getId();
    }
    
    @Override
    public void deleteUser(Long id, String extId) throws NCClientException, IOException {
        checkStatus(
            gson.fromJson(
                post(
                    "user/delete",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id),
                    Pair.of("extId", extId)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public void updateUser(
        long id, String firstName, String lastName, String avatarUrl, Map properties
    ) throws NCClientException, IOException {
        notNull(firstName, "firstName");
        notNull(lastName, "lastName");
    
        checkStatus(
            gson.fromJson(
                post(
                    "user/update",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id),
                    Pair.of("firstName", firstName),
                    Pair.of("lastName", lastName),
                    Pair.of("avatarUrl", avatarUrl),
                    Pair.of("properties", properties)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public void updateUserAdmin(Long id, boolean admin) throws NCClientException, IOException {
        checkStatus(
            gson.fromJson(
                post(
                    "user/admin",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id),
                    Pair.of("admin", admin)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public NCUser getUser(Long id, String extId) throws NCClientException, IOException {
        return
            checkAndExtract(
                post(
                    "user/get",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id),
                    Pair.of("usrExtId", extId)
                ),
                NCUserBean.class
            );
    }
    
    @Override
    public void resetUserPassword(Long id, String newPasswd) throws NCClientException, IOException {
        notNull(newPasswd, "newPasswd");
    
        checkStatus(
            gson.fromJson(
                post(
                    "user/passwd/reset",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id),
                    Pair.of("newPasswd", newPasswd)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public List getAllUsers() throws NCClientException, IOException {
        NCUsersAllBean b =
            checkAndExtract(
                post(
                    "user/all",
                    Pair.of("acsTok", acsTok)
                ),
                NCUsersAllBean.class
            );
        
        return new ArrayList<>(b.getUsers());
    }
    
    @Override
    public List getProbes() throws NCClientException, IOException {
        NCProbesAllBean b =
            checkAndExtract(
                post(
                    "probe/all",
                    Pair.of("acsTok", acsTok)
                ),
                NCProbesAllBean.class
            );
    
        return new ArrayList<>(b.getProbes());
    }
    
    @Override
    public void clearConversation(String mdlId, Long usrId, String usrExtId) throws NCClientException, IOException {
        notNull(mdlId, "mdlId");
        
        checkStatus(
            gson.fromJson(
                post(
                    "clear/conversation",
                    Pair.of("acsTok", acsTok),
                    Pair.of("mdlId", mdlId),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public void clearDialog(String mdlId, Long usrId, String usrExtId) throws NCClientException, IOException {
        notNull(mdlId, "mdlId");
        
        checkStatus(
            gson.fromJson(
                post(
                    "clear/dialog",
                    Pair.of("acsTok", acsTok),
                    Pair.of("mdlId", mdlId),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public void close() throws IOException, NCClientException {
        if (cancelOnExit)
            cancel(null, null, null);
    
        checkStatus(
            gson.fromJson(
                post(
                    "signout",
                    Pair.of("acsTok", acsTok)
                ),
                NCStatusResponseBean.class
            )
        );
    
        started = false;
    }

    @Override
    public String ask(String mdlId, String txt, Map data, boolean enableLog, Long usrId, String usrExtId) throws NCClientException, IOException {
        notNull(mdlId, "mdlId");
        notNull(txt, "txt");

        NCAskBean b =
            checkAndExtract(
                post(
                    "ask",
                    Pair.of("acsTok", acsTok),
                    Pair.of("txt", txt),
                    Pair.of("mdlId", mdlId),
                    Pair.of("data", data),
                    Pair.of("enableLog", enableLog),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCAskBean.class
            );

        return b.getServerRequestId();
    }

    @Override
    public NCResult askSync(String mdlId, String txt, Map data, boolean enableLog, Long usrId, String usrExtId) throws NCClientException, IOException {
        notNull(mdlId, "mdlId");
        notNull(txt, "txt");

        NCAskSyncBean b =
            checkAndExtract(
                post(
                    "ask/sync",
                    Pair.of("acsTok", acsTok),
                    Pair.of("txt", txt),
                    Pair.of("mdlId", mdlId),
                    Pair.of("data", data),
                    Pair.of("enableLog", enableLog),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCAskSyncBean.class
            );

        return b.getState();

    }

    @Override
    public long addFeedback(String srvReqId, double score, String comment, Long usrId, String usrExtId)
        throws NCClientException, IOException {
        notNull(srvReqId, "srvReqId");
        
        NCFeedbackAddBean b =
            checkAndExtract(
                post(
                    "feedback/add",
                    Pair.of("acsTok", acsTok),
                    Pair.of("srvReqId", srvReqId),
                    Pair.of("score", score),
                    Pair.of("comment", comment),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCFeedbackAddBean.class
            );
        
        return b.getId();
    }
    
    @Override
    public void deleteFeedback(Long id) throws NCClientException, IOException {
        checkStatus(
            gson.fromJson(
                post(
                    "feedback/delete",
                    Pair.of("acsTok", acsTok),
                    Pair.of("id", id)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public List getAllFeedback(String srvReqId, Long usrId, String usrExtId) throws NCClientException, IOException {
        NCFeedbackAllBean b =
            checkAndExtract(
                post(
                    "feedback/all",
                    Pair.of("acsTok", acsTok),
                    Pair.of("srvReqId", srvReqId),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCFeedbackAllBean.class
            );
        
        return new ArrayList<>(b.getFeedback());
    }
    
    @Override
    public List check(Set srvReqIds, Integer maxRows, Long usrId, String usrExtId) throws NCClientException, IOException {
        NCCheckBean b =
            checkAndExtract(
                post(
                    "check",
                    Pair.of("acsTok", acsTok),
                    Pair.of("srvReqIds", srvReqIds),
                    Pair.of("maxRows", maxRows),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCCheckBean.class
            );
        
        return new ArrayList<>(b.getStates());
    }
    
    @Override
    public void cancel(Set srvReqIds, Long usrId, String usrExtId) throws NCClientException, IOException {
        checkStatus(
            gson.fromJson(
                post(
                    "cancel",
                    Pair.of("acsTok", acsTok),
                    Pair.of("srvReqIds", srvReqIds),
                    Pair.of("usrId", usrId),
                    Pair.of("usrExtId", usrExtId)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public NCNewCompany addCompany(String name, String website, String country, String region, String city,
        String address, String postalCode, String adminEmail, String adminPasswd, String adminFirstName,
        String adminLastName, String adminAvatarUrl, Map props) throws IOException, NCClientException {
        notNull(name, "name");
        notNull(adminEmail, "adminEmail");
        notNull(adminPasswd, "adminPasswd");
        notNull(adminFirstName, "adminFirstName");
        notNull(adminLastName, "adminLastName");
        
        return
            checkAndExtract(
                post(
                    "company/add",
                    Pair.of("acsTok", acsTok),
                    Pair.of("name", name),
                    Pair.of("website", website),
                    Pair.of("country", country),
                    Pair.of("region", region),
                    Pair.of("city", city),
                    Pair.of("address", address),
                    Pair.of("postalCode", postalCode),
    
                    Pair.of("adminEmail", adminEmail),
                    Pair.of("adminPasswd", adminPasswd),
                    Pair.of("adminFirstName", adminFirstName),
                    Pair.of("adminLastName", adminLastName),
                    Pair.of("postalCode", adminAvatarUrl),
                    Pair.of("properties", props)
                ),
                NCTokenCreationBean.class
            );
    }
    
    @Override
    public NCCompany getCompany() throws IOException, NCClientException {
        return
            checkAndExtract(
                post(
                    "company/get",
                    Pair.of("acsTok", acsTok)
                ),
                NCCompanyBean.class
            );
    }
    
    @Override
    public void updateCompany(
        String name,
        String website,
        String country,
        String region,
        String city,
        String address,
        String postalCode,
        Map props
    ) throws IOException, NCClientException {
        notNull(name, "name");
    
        checkStatus(
            gson.fromJson(
                post(
                    "company/update",
                    Pair.of("acsTok", acsTok),
                    Pair.of("name", name),
                    Pair.of("website", website),
                    Pair.of("country", country),
                    Pair.of("region", region),
                    Pair.of("city", city),
                    Pair.of("address", address),
                    Pair.of("postalCode", postalCode),
                    Pair.of("properties", props)
                ),
                NCStatusResponseBean.class
            )
        );
    }
    
    @Override
    public String resetCompanyToken() throws IOException, NCClientException {
        NCCompanyTokenResetBean b =
            checkAndExtract(
                post(
                    "company/token/reset",
                    Pair.of("acsTok", acsTok)
                ),
                NCCompanyTokenResetBean.class
            );
        
        return b.getToken();
    }
    
    @Override
    public void deleteCompany() throws IOException, NCClientException {
        checkStatus(
            gson.fromJson(
                post(
                    "company/delete",
                    Pair.of("acsTok", acsTok)
                ),
                NCStatusResponseBean.class
            )
        );
    }

    @Override
    public NCSuggestionData suggestSynonyms(String mdlId, Double minScore) throws NCClientException, IOException {
        NCSuggestionResultBean res = checkAndExtract(
            post(
                "model/sugsyn",
                Pair.of("acsTok", acsTok),
                Pair.of("mdlId", mdlId),
                Pair.of("minScore", minScore)
            ),
            NCSuggestionResultBean.class
        );

        return res.getResult();
    }

    @Override
    public NCElementSynonymsData getSynonyms(String mdlId, String elmId) throws NCClientException, IOException {
        return checkAndExtract(
            post(
                "model/syns",
                Pair.of("acsTok", acsTok),
                Pair.of("mdlId", mdlId),
                Pair.of("elmId", elmId)
            ),
            NCElementSynonymsDataBean.class
        );
    }

    @Override
    public NCModelInfo getModelInfo(String mdlId) throws NCClientException, IOException {
        NCModelInfoResultBean res = checkAndExtract(
            post(
                "model/info",
                Pair.of("acsTok", acsTok),
                Pair.of("mdlId", mdlId)
            ),
            NCModelInfoResultBean.class
        );

        return res.getModel();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy