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

io.getlime.push.client.PushServerClient Maven / Gradle / Ivy

/*
 * Copyright 2016 Wultra s.r.o.
 *
 * Licensed 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 io.getlime.push.client;

import com.fasterxml.jackson.databind.Module;
import com.wultra.core.rest.client.base.DefaultRestClient;
import com.wultra.core.rest.client.base.RestClient;
import com.wultra.core.rest.client.base.RestClientConfiguration;
import com.wultra.core.rest.client.base.RestClientException;
import io.getlime.core.rest.model.base.entity.Error;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
import io.getlime.core.rest.model.base.response.Response;
import io.getlime.push.model.base.PagedResponse;
import io.getlime.push.model.entity.*;
import io.getlime.push.model.enumeration.ApnsEnvironment;
import io.getlime.push.model.enumeration.MobilePlatform;
import io.getlime.push.model.enumeration.Mode;
import io.getlime.push.model.request.*;
import io.getlime.push.model.response.*;
import io.getlime.push.model.validator.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.List;

/**
 * Simple class for interacting with the push server RESTful API.
 *
 * @author Petr Dvorak, [email protected]
 * @author Martin Tupy, [email protected]
 */
public class PushServerClient {

    private static final Logger logger = LoggerFactory.getLogger(PushServerClient.class);

    private final RestClient restClient;

    /**
     * Main constructor with the push server base URL.
     * @param serviceBaseUrl Push server instance base URL.
     * @throws PushServerClientException Thrown in case REST client initialization fails.
     */
    public PushServerClient(String serviceBaseUrl) throws PushServerClientException {
        try {
            this.restClient = DefaultRestClient.builder().baseUrl(serviceBaseUrl).build();
        } catch (RestClientException ex) {
            throw new PushServerClientException("Rest client initialization failed, error: " + ex.getMessage(), ex);
        }
    }

    /**
     * Construct the push server client with the given configuration.
     *
     * @param config REST client configuration.
     * @param modules Optional jackson modules.
     * @throws PushServerClientException Thrown in case REST client initialization fails.
     */
    public PushServerClient(final RestClientConfiguration config, final Module... modules) throws PushServerClientException {
        try {
            this.restClient = new DefaultRestClient(config, modules);
        } catch (RestClientException ex) {
            throw new PushServerClientException("Rest client initialization failed, error: " + ex.getMessage(), ex);
        }
    }

    // Client calls

    /**
     * Returns service information
     *
     * @return True if service is running.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse getServiceStatus() throws PushServerClientException {

        logger.info("Calling push server status service - start");
        final ObjectResponse result = getObjectImpl("/push/service/status", null, ServiceStatusResponse.class);
        logger.info("Calling push server status service - finish");

        return result;
    }

    /**
     * Register anonymous device to the push server.
     *
     * @param appId PowerAuth application app ID.
     * @param token Token received from the push service provider (APNs, FCM).
     * @param platform Mobile platform (iOS, Android).
     * @return True if device registration was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean createDevice(String appId, String token, MobilePlatform platform) throws PushServerClientException {
        return createDevice(appId, token, platform, null);
    }

    /**
     * Register device associated with activation ID to the push server.
     *
     * @param appId PowerAuth application app ID.
     * @param token Token received from the push service provider (APNs, FCM).
     * @param platform Mobile platform (iOS, Android).
     * @param activationId PowerAuth activation ID.
     * @return True if device registration was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean createDevice(String appId, String token, MobilePlatform platform, String activationId) throws PushServerClientException {
        CreateDeviceRequest request = new CreateDeviceRequest();
        request.setAppId(appId);
        request.setToken(token);
        request.setPlatform(platform);
        request.setActivationId(activationId);

        // Validate request on the client side.
        String error = CreateDeviceRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling create device service, appId: {}, token: {}, platform: {} - start", appId, maskToken(token), platform);
        Response response = postObjectImpl("/push/device/create", new ObjectRequest<>(request));
        logger.info("Calling create device service, appId: {}, token: {}, platform: {} - finish", appId, maskToken(token), platform);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Register device associated with multiple activation IDs to the push server.
     *
     * @param appId PowerAuth application app ID.
     * @param token Token received from the push service provider (APNs, FCM).
     * @param platform Mobile platform (iOS, Android).
     * @param activationIds PowerAuth activation IDs.
     * @return True if device registration was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean createDeviceForActivations(String appId, String token, MobilePlatform platform, List activationIds) throws PushServerClientException {
        CreateDeviceForActivationsRequest request = new CreateDeviceForActivationsRequest();
        request.setAppId(appId);
        request.setToken(token);
        request.setPlatform(platform);
        request.getActivationIds().addAll(activationIds);

        // Validate request on the client side.
        String error = CreateDeviceRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling create device service, appId: {}, token: {}, platform: {} - start", appId, maskToken(token), platform);
        Response response = postObjectImpl("/push/device/create/multi", new ObjectRequest<>(request));
        logger.info("Calling create device service, appId: {}, token: {}, platform: {} - finish", appId, maskToken(token), platform);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Remove device from the push server
     *
     * @param appId PowerAuth application app ID.
     * @param token Token received from the push service provider.
     * @return True if device removal was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean deleteDevice(String appId, String token) throws PushServerClientException {
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.setAppId(appId);
        request.setToken(token);

        // Validate request on the client side.
        String error = DeleteDeviceRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server delete device service, appId: {}, token: {} - start", appId, maskToken(token));
        Response response = postObjectImpl("/push/device/delete", new ObjectRequest<>(request));
        logger.info("Calling push server delete device service, appId: {}, token: {} - finish", appId, maskToken(token));

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Update activation status for given device registration.
     *
     * @param activationId Identifier of activation
     * @return True if updating went successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean updateDeviceStatus(String activationId) throws PushServerClientException {
        UpdateDeviceStatusRequest request = new UpdateDeviceStatusRequest();
        request.setActivationId(activationId);

        // Validate request on the client side.
        String error = UpdateDeviceStatusRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server update device status, activation ID: {} - start", activationId);
        // Note that there is just plain 'request' in the request, not 'new ObjectRequest<>(request)'.
        // This is due to the fact that standard PowerAuth Server callback format is used here.
        final Response response = postImpl("/push/device/status/update", request, new ParameterizedTypeReference<>(){});
        logger.info("Calling push server update device status, activation ID: {} - finish", activationId);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Send a single push message to application with given ID.
     *
     * @param appId PowerAuth application app ID.
     * @param pushMessage Push message to be sent.
     * @return SendMessageResponse in case everything went OK, ErrorResponse in case of an error.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse sendPushMessage(String appId, PushMessage pushMessage) throws PushServerClientException {
        return sendPushMessage(appId, Mode.SYNCHRONOUS, pushMessage);
    }

    /**
     * Send a single push message to application with given ID.
     *
     * @param appId PowerAuth application app ID.
     * @param mode Mode of push notification sending.
     * @param pushMessage Push message to be sent.
     * @return SendMessageResponse in case everything went OK, ErrorResponse in case of an error.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse sendPushMessage(String appId, Mode mode, PushMessage pushMessage) throws PushServerClientException {
        final SendPushMessageRequest request = new SendPushMessageRequest();
        request.setMode(mode);
        request.setAppId(appId);
        request.setMessage(pushMessage);

        // Validate request on the client side.
        final String error = SendPushMessageRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server to send a push message, app ID: {}, user ID: {} - start", appId, pushMessage.getUserId());
        final ObjectResponse result = postObjectImpl("/push/message/send", new ObjectRequest<>(request), PushMessageSendResult.class);
        logger.info("Calling push server to send a push message, app ID: {}, user ID: {} - finish", appId, pushMessage.getUserId());

        return result;
    }

    /**
     * Send a push message batch to application with given ID.
     *
     * @param appId PowerAuth application app ID.
     * @param batch Push message batch to be sent.
     * @return SendMessageResponse in case everything went OK, ErrorResponse in case of an error.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse sendPushMessageBatch(String appId, List batch) throws PushServerClientException {
        return sendPushMessageBatch(appId, Mode.SYNCHRONOUS, batch);
    }

    /**
     * Send a push message batch to application with given ID.
     *
     * @param appId PowerAuth application app ID.
     * @param mode Mode of push notification sending.
     * @param batch Push message batch to be sent.
     * @return SendMessageResponse in case everything went OK, ErrorResponse in case of an error.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse sendPushMessageBatch(String appId, Mode mode, List batch) throws PushServerClientException {
        final SendPushMessageBatchRequest request = new SendPushMessageBatchRequest();
        request.setAppId(appId);
        request.setMode(mode);
        request.setBatch(batch);

        // Validate request on the client side.
        final String error = SendPushMessageBatchRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server to send a push message batch, app ID: {} - start", appId);
        final ObjectResponse result = postObjectImpl("/push/message/batch/send", new ObjectRequest<>(request), PushMessageSendResult.class);
        logger.info("Calling push server to send a push message batch, app ID: {} - finish", appId);

        return result;
    }

    /**
     * Create a campaign.
     *
     * @param appId Application ID.
     * @param message Message which attributes are defined in PushMessageBody.
     * @return ID of new created campaign.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse createCampaign(String appId, PushMessageBody message) throws PushServerClientException {
        CreateCampaignRequest request = new CreateCampaignRequest();
        request.setAppId(appId);
        request.setMessage(message);

        // Validate request on the client side.
        String error = CreateCampaignRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server to create a push campaign, app ID: {} - start", appId);
        final ObjectResponse result = postObjectImpl("/push/campaign/create", new ObjectRequest<>(request), CreateCampaignResponse.class);
        logger.info("Calling push server to create a push campaign, app ID: {} - finish", appId);

        return result;
    }

    /**
     * Delete a campaign specified with campaignId.
     *
     * @param campaignId Campaign ID.
     * @return True if campaign is removed, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean deleteCampaign(Long campaignId) throws PushServerClientException {
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);

        logger.info("Calling push server to delete a push campaign, campaign ID: {} - start", campaignId);
        final ObjectResponse response = postObjectImpl("/push/campaign/" + campaignIdSanitized + "/delete", null, DeleteCampaignResponse.class);
        logger.info("Calling push server to delete a push campaign, campaign ID: {} - finish", campaignId);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Get list of campaigns, dependent on all param
     *
     * @param all true to get whole list, false to get campaigns that are only sent
     * @return List of campaigns.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse getListOfCampaigns(boolean all) throws PushServerClientException {
        MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("all", Collections.singletonList(Boolean.valueOf(all).toString()));

        logger.info("Calling push server to obtain a push campaign list - start");
        final ObjectResponse result = getObjectImpl("/push/campaign/list", params, ListOfCampaignsResponse.class);
        logger.info("Calling push server to obtain a push campaign list - finish");

        return result;
    }

    /**
     * Get a campaign specified with campaignID.
     *
     * @param campaignId ID of campaign to get.
     * @return Details of campaign, defined in CampaignResponse
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public ObjectResponse getCampaign(Long campaignId) throws PushServerClientException {
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);

        logger.info("Calling push server to obtain a push campaign detail, campaign ID: {} - start", campaignId);
        final ObjectResponse result = getObjectImpl("/push/campaign/" + campaignIdSanitized + "/detail", null, CampaignResponse.class);
        logger.info("Calling push server to obtain a push campaign detail, campaign ID: {} - finish", campaignId);

        return result;
    }

    /**
     * Add a list of users to a specific campaign
     *
     * @param campaignId Identifier of campaign.
     * @param users List of users to add.
     * @return True if adding was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean addUsersToCampaign(Long campaignId, List users) throws PushServerClientException {
        final ListOfUsers listOfUsers = new ListOfUsers(users);
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);

        logger.info("Calling push server to add users to campaign, campaign ID: {} - start", campaignId);
        final Response response = putObjectImpl("/push/campaign/" + campaignIdSanitized + "/user/add", new ObjectRequest<>(listOfUsers));
        logger.info("Calling push server to add users to campaign, campaign ID: {} - finish", campaignId);

        if (response == null) {
            throw new PushServerClientException(new Error("PUSH_SERVER_CLIENT_ERROR", "Network communication has failed."));
        }

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Get a list of users in paged format from specific campaign
     *
     * @param campaignId Identifier of campaign.
     * @param page Page number.
     * @param size Size of elements per page.
     * @return Page of users specified with params.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public PagedResponse getListOfUsersFromCampaign(Long campaignId, int page, int size) throws PushServerClientException {
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);
        final MultiValueMap params = buildPages(page, size);

        final ParameterizedTypeReference> typeReference = new ParameterizedTypeReference<>() {};
        logger.info("Calling push server to get users from the campaign, campaign ID: {} - start", campaignId);
        final PagedResponse result = getImpl("/push/campaign/" + campaignIdSanitized + "/user/list", params, typeReference);
        logger.info("Calling push server to get users from the campaign, campaign ID: {} - finish", campaignId);

        return result;
    }

    /**
     * Delete a list of users from specific campaign.
     *
     * @param campaignId Identifier of campaign.
     * @param users List of users' Identifiers to delete.
     * @return True if deletion was successful, false otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean deleteUsersFromCampaign(Long campaignId, List users) throws PushServerClientException {
        final ListOfUsers listOfUsers = new ListOfUsers(users);
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);

        logger.info("Calling push server to remove users from the campaign, campaign ID: {} - start", campaignId);
        final Response response = postObjectImpl("/push/campaign/" + campaignIdSanitized + "/user/delete", new ObjectRequest<>(listOfUsers));
        logger.info("Calling push server to remove users from the campaign, campaign ID: {} - finish", campaignId);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Send a campaign on test user for trying its correctness.
     *
     * @param campaignId Identifier of campaign.
     * @param userId Identifier of test user.
     * @return True if sent, else otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean sendTestCampaign(Long campaignId, String userId) throws PushServerClientException {
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);
        final TestCampaignRequest request = new TestCampaignRequest();
        request.setUserId(userId);

        // Validate request on the client side.
        final String error = TestCampaignRequestValidator.validate(request);
        if (error != null) {
            throw new PushServerClientException(error);
        }

        logger.info("Calling push server to send test campaign, campaign ID: {}, user ID: {} - start", campaignId, userId);
        final Response response = postObjectImpl("/push/campaign/send/test/" + campaignIdSanitized, new ObjectRequest<>(request));
        logger.info("Calling push server to send test campaign, campaign ID: {}, user ID: {} - finish", campaignId, userId);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Send a specific campaign to users carrying this campaignID in PushCampaignUser model, but only once per device identified by token.
     *
     * @param campaignId Identifier of campaign.
     * @return True if sent, else otherwise.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    public boolean sendCampaign(Long campaignId) throws PushServerClientException {
        final String campaignIdSanitized = URLEncoder.encode(String.valueOf(campaignId), StandardCharsets.UTF_8);

        logger.info("Calling push server to send a production campaign, campaign ID: {} - start", campaignId);
        final Response response = postObjectImpl("/push/campaign/send/live/" + campaignIdSanitized, null);
        logger.info("Calling push server to send a production campaign, campaign ID: {} - finish", campaignId);

        return response.getStatus().equals(Response.Status.OK);
    }

    /**
     * Get list of application credentials entities.
     * @return Application credentials entity list.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse getApplicationList() throws PushServerClientException {
        logger.info("Calling push server to retrieve list of applications - start");
        final ObjectResponse response = getObjectImpl("/admin/app/list", null, GetApplicationListResponse.class);
        logger.info("Calling push server to retrieve list of applications - finish");
        return response;
    }

    /**
     * Get list of applications which are not yet configured in Push Server but exist in PowerAuth server.
     * @return List of applications which are not configured.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse getUnconfiguredApplicationList() throws PushServerClientException {
        logger.info("Calling push server to retrieve list of unconfigured applications - start");
        final ObjectResponse response = getObjectImpl("/admin/app/unconfigured/list", null, GetApplicationListResponse.class);
        logger.info("Calling push server to retrieve list of unconfigured applications - finish");
        return response;
    }

    /**
     * Get detail for an application credentials entity.
     * @param request Application detail request.
     * @return Application credentials entity detail.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse getApplicationDetail(final GetApplicationDetailRequest request) throws PushServerClientException {
        logger.info("Calling push server to retrieve application detail, ID: {} - start", request.getAppId());
        final ObjectResponse response = postObjectImpl("/admin/app/detail", new ObjectRequest<>(request), GetApplicationDetailResponse.class);
        logger.info("Calling push server to retrieve application detail, ID: {} - finish", request.getAppId());
        return response;
    }

    /**
     * Create application credentials entity.
     * @param appId PowerAuth application ID.
     * @return Response with ID of created application credentials entity.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse createApplication(String appId) throws PushServerClientException {
        final CreateApplicationRequest request = new CreateApplicationRequest(appId);
        logger.info("Calling push server to create application, app ID: {} - start", appId);
        final ObjectResponse response = postObjectImpl("/admin/app/create", new ObjectRequest<>(request), CreateApplicationResponse.class);
        logger.info("Calling push server to create application, app ID: {} - finish", appId);
        return response;
    }

    /**
     * Update iOS details for an application credentials entity.
     * @param appId ID of application credentials entity.
     * @param bundle The iOS bundle record.
     * @param keyId The iOS key record.
     * @param teamId The iOS team ID record.
     * @param environment The APNs environment.
     * @param privateKey The iOS private key bytes.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response updateIos(String appId, String bundle, String keyId, String teamId, ApnsEnvironment environment, byte[] privateKey) throws PushServerClientException {
        final String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey);
        final UpdateIosRequest request = new UpdateIosRequest(appId, bundle, keyId, teamId, environment, privateKeyBase64);
        logger.info("Calling push server to update iOS, ID: {} - start", appId);
        final Response response = putObjectImpl("/admin/app/ios/update", new ObjectRequest<>(request));
        logger.info("Calling push server to update iOS, ID: {} - finish", appId);
        return response;
    }

    /**
     * Remove iOS record from an application credentials entity.
     * @param appId Application credentials entity ID.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response removeIos(String appId) throws PushServerClientException {
        final RemoveIosRequest request = new RemoveIosRequest(appId);
        logger.info("Calling push server to remove iOS, ID: {} - start", appId);
        final Response response = postObjectImpl("/admin/app/ios/remove", new ObjectRequest<>(request));
        logger.info("Calling push server to remove iOS, ID: {} - finish", appId);
        return response;
    }

    /**
     * Update Android details for an application credentials entity.
     * @param appId Application credentials entity ID.
     * @param projectId The Android project ID record.
     * @param privateKey The Android private key bytes.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response updateAndroid(String appId, String projectId, byte[] privateKey) throws PushServerClientException {
        final String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey);
        final UpdateAndroidRequest request = new UpdateAndroidRequest(appId, projectId, privateKeyBase64);
        logger.info("Calling push server to update android, ID: {} - start", appId);
        final Response response = putObjectImpl("/admin/app/android/update", new ObjectRequest<>(request));
        logger.info("Calling push server to update android, ID: {} - finish", appId);
        return response;
    }

    /**
     * Remove Android record from an application credentials entity.
     * @param appId Application credentials entity ID.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response removeAndroid(String appId) throws PushServerClientException {
        final RemoveAndroidRequest request = new RemoveAndroidRequest(appId);
        logger.info("Calling push server to remove android, ID: {} - start", appId);
        final Response response = postObjectImpl("/admin/app/android/remove", new ObjectRequest<>(request));
        logger.info("Calling push server to remove android, ID: {} - finish", appId);
        return response;
    }

    /**
     * Update Huawei details for an application credentials entity.
     *
     * @param request Update Huawei request.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response updateHuawei(final UpdateHuaweiRequest request) throws PushServerClientException {
        logger.info("Calling push server to update Huawei, ID: {} - start", request.getAppId());
        final Response response = putObjectImpl("/admin/app/huawei/update", new ObjectRequest<>(request));
        logger.info("Calling push server to update Huawei, ID: {} - finish", request.getAppId());
        return response;
    }

    /**
     * Remove Huawei record from an application credentials entity.
     *
     * @param appId Application credentials entity ID.
     * @return Response from server.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response removeHuawei(String appId) throws PushServerClientException {
        final RemoveHuaweiRequest request = new RemoveHuaweiRequest(appId);
        logger.info("Calling push server to remove Huawei, ID: {} - start", appId);
        final Response response = postObjectImpl("/admin/app/huawei/remove", new ObjectRequest<>(request));
        logger.info("Calling push server to remove Huawei, ID: {} - finish", appId);
        return response;
    }

    /**
     * Post a message to an inbox of provided user.
     * @param request Request with the message detail.
     * @return Response with a newly created message.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse postMessage(CreateInboxMessageRequest request) throws PushServerClientException {
        logger.info("Calling push server to send message to inbox of: {}, subject: {} - start", request.getUserId(), request.getSubject());
        final ObjectResponse response = postObjectImpl("/inbox/messages", new ObjectRequest<>(request), GetInboxMessageDetailResponse.class);
        logger.info("Calling push server to send message to inbox of: {}, subject: {} - finish", request.getUserId(), request.getSubject());
        return response;
    }

    /**
     * Fetch the list of messages for a given user.
     * @param userId User ID.
     * @param applications List of application IDs.
     * @param onlyUnread Indication if only unread messages should be returneed.
     * @param page Page index.
     * @param size Page size.
     * @return List of inbox messages.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public PagedResponse fetchMessageListForUser(String userId, List applications, boolean onlyUnread, Integer page, Integer size) throws PushServerClientException {
        final MultiValueMap params = buildPages(page, size);
        params.add("userId", userId);
        params.add("applications", String.join(",", applications));
        params.add("onlyUnread", Boolean.toString(onlyUnread));

        final ParameterizedTypeReference> typeReference = new ParameterizedTypeReference<>() {};
        logger.info("Calling push server fetch messages for user: {} - start", userId);
        final PagedResponse result = getImpl("/inbox/messages/list", params, typeReference);
        logger.info("Calling push server fetch messages for user: {} - finish", userId);

        return result;
    }

    /**
     * Fetch unread message count for a user with given ID.
     * @param userId User ID.
     * @param appId Application ID.
     * @return Count of unread messages.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse fetchMessageCountForUser(String userId, String appId) throws PushServerClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.add("userId", userId);
        params.add("appId", appId);

        final ParameterizedTypeReference> typeReference = new ParameterizedTypeReference<>() {};
        logger.info("Calling push server fetch message count for user: {} - start", userId);
        final ObjectResponse result = getImpl("/inbox/messages/count", params, typeReference);
        logger.info("Calling push server fetch message count for user: {} - finish", userId);

        return result;
    }

    /**
     * Read all unread messages in inbox of provided user.
     * @param userId User ID.
     * @param appId Application ID.
     * @return Response.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public Response readAllMessages(String userId, String appId) throws PushServerClientException {
        final ReadAllInboxMessagesRequest request = new ReadAllInboxMessagesRequest();
        request.setUserId(userId);
        request.setAppId(appId);
        logger.info("Calling push server to mark all messages read in inbox of user: {} - start", userId);
        final Response response = postObjectImpl("/inbox/messages/read-all", new ObjectRequest<>(request));
        logger.info("Calling push server to mark all messages read in inbox of user: {} - finish", userId);
        return response;
    }

    /**
     * Fetch detail of the message for given message ID.
     * @param inboxId Inbox message ID.
     * @return Detail of a message with given ID.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse fetchMessageDetail(String inboxId) throws PushServerClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.add("id", inboxId);

        logger.info("Calling push server fetch message ID: {} - start", inboxId);
        final ObjectResponse result = getObjectImpl("/inbox/messages/detail", params, GetInboxMessageDetailResponse.class);
        logger.info("Calling push server fetch message ID: {} - finish", inboxId);

        return result;
    }

    /**
     * Read message with given ID in inbox of provided user.
     * @param inboxId Inbox message ID.
     * @return Detail of the read message.
     * @throws PushServerClientException Thrown when communication with Push Server fails.
     */
    public ObjectResponse readMessage(String inboxId) throws PushServerClientException {
        final ReadInboxMessageRequest request = new ReadInboxMessageRequest();
        request.setInboxId(inboxId);
        logger.info("Calling push server to read message to inbox of: {} - start", inboxId);
        final ObjectResponse response = postObjectImpl("/inbox/messages/read", new ObjectRequest<>(request), GetInboxMessageDetailResponse.class);
        logger.info("Calling push server to read message to inbox of: {} - finish", inboxId);
        return response;
    }

    // Generic HTTP client methods

    /**
     * Prepare GET response.
     *
     * @param url specific url of method.
     * @param params params to pass to url path, optional.
     * @param typeReference response type reference.
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     *
     */
    private  T getImpl(String url, MultiValueMap params, ParameterizedTypeReference typeReference) throws PushServerClientException {
        try {
            return restClient.get(url, params, null, typeReference).getBody();
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP GET request failed."));
        }
    }

    /**
     * Prepare GET object response.
     *
     * @param url specific url of method.
     * @param params params to pass to url path, optional.
     * @param responseType response type.
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     *
     */
    private  ObjectResponse getObjectImpl(String url, MultiValueMap params, Class responseType) throws PushServerClientException {
        try {
            return restClient.getObject(url, params, null, responseType);
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP GET request failed."));
        }
    }

    /**
     * Prepare a generic POST response.
     *
     * @param url specific url of method
     * @param request request body
     * @param typeReference type reference
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private  T postImpl(String url, Object request, ParameterizedTypeReference typeReference) throws PushServerClientException {
        try {
            return restClient.post(url, request, typeReference).getBody();
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Prepare POST object response. Uses default {@link Response} type reference for response.
     *
     * @param url specific url of method
     * @param request request body
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private Response postObjectImpl(String url, ObjectRequest request) throws PushServerClientException {
        try {
            return restClient.postObject(url, request);
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Prepare POST object response.
     *
     * @param url specific url of method
     * @param request request body
     * @param responseType response type
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private  ObjectResponse postObjectImpl(String url, ObjectRequest request, Class responseType) throws PushServerClientException {
        try {
            return restClient.postObject(url, request, responseType);
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Prepare a generic PUT response.
     *
     * @param url specific url of method
     * @param request request body
     * @param typeReference type reference
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private  T putImpl(String url, Object request, ParameterizedTypeReference typeReference) throws PushServerClientException {
        try {
            return restClient.put(url, request, typeReference).getBody();
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Prepare a generic PUT response.
     *
     * @param url specific url of method
     * @param request request body
     * @param queryParams query parameters
     * @param headers HTTP headers
     * @param typeReference type reference
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private  T putImpl(String url, Object request, MultiValueMap queryParams, MultiValueMap headers, ParameterizedTypeReference typeReference) throws PushServerClientException {
        try {
            return restClient.put(url, request, queryParams, headers, typeReference).getBody();
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Prepare PUT object response. Uses default {@link Response} type reference for response.
     *
     * @param url specific url of method
     * @param request request body
     * @return Object obtained after processing the response JSON.
     * @throws PushServerClientException In case of network, response / JSON processing, or other IO error.
     */
    private Response putObjectImpl(String url, ObjectRequest request) throws PushServerClientException {
        try {
            return restClient.putObject(url, request);
        } catch (RestClientException ex) {
            logger.debug(ex.getMessage(), ex);
            throw new PushServerClientException(ex, new Error("PUSH_SERVER_CLIENT_ERROR", "HTTP POST request failed."));
        }
    }

    /**
     * Mask push service token to avoid leaking tokens in log files.
     * @param token Push service token.
     * @return Masked push service token.
     */
    private String maskToken(String token) {
        if (token == null || token.length() < 10) {
            return token;
        }
        return token.substring(0, 10) + "...";
    }

    /**
     * Convert input page and size to query parameter map.
     * @param page Page index. Zero indexed. Default: 0.
     * @param size Page size. Default: 100.
     * @return Query parameter map.
     */
    private MultiValueMap buildPages(Integer page, Integer size) {
        final MultiValueMap result = new LinkedMultiValueMap<>();
        if (page != null) {
            result.put("page", Collections.singletonList(page.toString()));
        } else {
            result.put("page", Collections.singletonList("0"));
        }
        if (size != null) {
            result.put("size", Collections.singletonList(size.toString()));
        } else {
            result.put("size", Collections.singletonList("100"));
        }
        return result;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy