6 Replies Latest reply on Feb 15, 2015 8:14 AM by moshe.peretz

    Custom error message from ESP in Jive

    kamilwylegala

      Hi,

       

      I work on ESP for Jive. I would like to change content of default error message which is given by Jive in case of error.

       

      For example I would like to change default message when user uploads file with forbidden name in our ESP.

       

      Default message is: "Internal server error: The requested action could not be completed due to an error on your external storage provider. Please try again. If this error persists, please contact your community administrator."

       

      Please tell me how I should change response body (or headers) to modify this message. I was trying few approaches (e.g. {"message": "Custom error"} and so on...) but nothing is happening.

       

      Regards,

      Kamil.

        • Re: Custom error message from ESP in Jive
          ztenerowicz

          We need to return custom messages and those messages should also be able to allow triggering actions or just have links in them.

           

          If our ESP doesn't find a user by the email used for impersonation, the error needs to be informative AND it needs to let user open an app screen where she can fix the mapping for this email by logging in to External Storage Provider manually.

          So we actually need to open an app for that after a certain kind of error occurs.

          • Re: Custom error message from ESP in Jive

            hi Kamil,

             

            This sounds like a great improvement, I filed a ticket for the engineering / product teams to take a look at this (JENSO-7159).

             

            AJ

              • Re: Custom error message from ESP in Jive
                ztenerowicz

                So, for clarification, currently it's not possible to send any string of text to Jive in response body of a http 4xx or 5xx so that it'd be shown to the user who triggered the action?

                Not even in case we set the error reason to "UNKNOWN"?

                 

                The docs stated that there's a message field in that ExStorageError JSON object too, but we were unable to make it show, ever.

                  • Re: Custom error message from ESP in Jive

                    could be, I tracked down where *I think* error handling happens when we call out to the remote server:

                     

                        private void processHttpErrors(URI uri, HttpResponse httpResponse)

                        throws ExStorageException

                        {

                            // check if the status code is an error code

                            int statusCode = httpResponse.getStatusLine().getStatusCode();

                            if (statusCode >= HTTP_STATUS_ERROR_LOWER_BOUND)

                            {

                                // create an http exception

                                String rawResponse = getRawResponse(httpResponse);

                                ExStorageHttpException exStorageHttpException = new ExStorageHttpException(httpResponse.getStatusLine().getReasonPhrase(), statusCode, rawResponse);

                     

                     

                                log.error("Received " + statusCode + " from " + uri + ". Raw response is: " + rawResponse);

                     

                     

                                // if unauthorized

                                if (statusCode == HttpStatus.SC_UNAUTHORIZED)

                                    throw new ExStorageTokenExpiredException(exStorageHttpException);

                     

                     

                                // if unexpected

                                if (statusCode > HTTP_STATUS_UNEXPECTED_ERROR_LOWER_BOUND)

                                    throw new ExStorageServiceUnavailableException(exStorageHttpException);

                     

                     

                                // check if the response contains the ExStorageErrorEntity object

                                ExStorageErrorEntity error = getExStorageError(rawResponse, httpResponse.getAllHeaders());

                     

                     

                                // if not, throw the http exception

                                if (error == null || error.getReason() == null)

                                    throw exStorageHttpException;

                     

                     

                                // if so, throw the appropriate ExStorageException implementation

                                exStorageHttpException.setExStorageError(error);

                     

                     

                                switch (error.getReason()) {

                                    case PROVIDER_UNAVAILABLE:

                                        throw new ExStorageEndProviderUnavailableException(exStorageHttpException);

                                    case PROVIDER_UNAUTHORIZED:

                                        throw new ExStorageEndProviderUnauthorizedException(exStorageHttpException);

                                    case UNAUTHORIZED:

                                        throw new ExStorageTokenExpiredException(exStorageHttpException);

                                    case NOT_FOUND:

                                        throw new ExStorageItemNotFoundException(exStorageHttpException);

                                    case ILLEGAL_NAME:

                                        throw new ExStorageIllegalNameException(exStorageHttpException);

                                    case ILLEGAL_CONTENT_TYPE:

                                        throw new ExStorageIllegalContentTypeException(exStorageHttpException);

                                    case ALREADY_EXISTS:

                                        throw new ExStorageAlreadyExistsException(exStorageHttpException);

                                    case QUOTA_EXCEEDED:

                                        throw new ExStorageQuotaExceededException(exStorageHttpException);

                                    case RESOURCE_OUT_OF_SYNC:

                                        throw new ExStorageEtagMismatchException(exStorageHttpException);

                                    case LOCKED:

                                        throw new ExStorageItemLockedException(exStorageHttpException);

                                    case RESOURCE_TEMPORARILY_UNAVAILABLE:

                                        throw new ExStorageResourceTemporarilyUnavailableException(exStorageHttpException);

                                    case MISSING_CONFIGURATION:

                                        throw new ExStorageMissingConfigurationException(exStorageHttpException);

                                    case HEALTHCHECK_ERROR:

                                        throw new HealthCheckExStorageException(error.getMessage(), exStorageHttpException);

                                    case PROVIDER_BUSY:

                                        throw new ExStorageProviderBusyException(exStorageHttpException);

                                    case UNKNOWN:

                                        throw new ExStorageException(error.getMessage(), exStorageHttpException);

                                }

                            }

                        }

                     

                     

                        private ExStorageErrorEntity getExStorageError(String rawResponse, Header[] headers) {

                            if (StringUtils.isNullOrEmpty(rawResponse)) {

                                return null;

                            }

                     

                     

                            try (InputStream content = IOUtils.toInputStream(rawResponse)) {

                                return ResponseReadingStrategy.GsonReadingStrategy.readFromResponse(content, headers, ExStorageErrorEntity.class);

                            }

                            catch (Exception e) {

                                log.error("Failed to read ExStorageError from response", e);

                                return null;

                            }

                        }

                    and then knowing that (and the class name you gave me), I can see some errors where it looks like we're having a hard time reading what's in the response:

                     

                    2015-02-09 16:55:55,326 [http-nio-0.0.0.0-9200-exec-10] [1:ztenerowicz+1@egnyte.com:REGULAR] ERROR com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI - Failed to read ExStorageError from response

                    com.google.gson.JsonParseException: Failed parsing JSON source: java.io.InputStreamReader@15c45402 to Json

                            at com.google.gson.JsonParser.parse(JsonParser.java:59)

                            at com.google.gson.Gson.fromJson(Gson.java:443)

                            at com.google.gson.Gson.fromJson(Gson.java:419)

                            at com.jivesoftware.community.integration.storage.extensions.GsonReadingStrategy.readFromResponse(GsonReadingStrategy.java:38)

                            at com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI.getExStorageError(ExStorageBaseAPI.java:454)

                            at com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI.processHttpErrors(ExStorageBaseAPI.java:404)

                            at com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI.callRestAPI(ExStorageBaseAPI.java:329)

                            at com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI.wrapAndCallRestAPI(ExStorageBaseAPI.java:311)

                            at com.jivesoftware.community.integration.storage.api.ExStorageBaseAPI.wrapAndCallRestAPI(ExStorageBaseAPI.java:301)

                            at com.jivesoftware.community.integration.storage.api.ExStorageAPI.upload(ExStorageAPI.java:183)

                            at com.jivesoftware.community.integration.storage.api.ExStorageAPI.uploadAndCache(ExStorageAPI.java:161)

                            at com.jivesoftware.community.integration.storage.api.ExStorageAPI.uploadFile(ExStorageAPI.java:87)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.callFileUpload(ExStorageFileManagerImpl.java:337)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.stream_aroundBody2(ExStorageFileManagerImpl.java:287)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl$AjcClosure3.run(ExStorageFileManagerImpl.java:1)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:60)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:66)

                            at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:64)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.stream(ExStorageFileManagerImpl.java:274)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.upload_aroundBody0(ExStorageFileManagerImpl.java:261)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl$AjcClosure1.run(ExStorageFileManagerImpl.java:1)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:60)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:66)

                            at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)

                            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:64)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.upload(ExStorageFileManagerImpl.java:256)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.upload(ExStorageFileManagerImpl.java:252)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.upload(ExStorageFileManagerImpl.java:247)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpl.uploadFile(ExStorageFileManagerImpl.java:234)

                            at com.jivesoftware.community.integration.storage.file.impl.ExStorageFileManagerImpersonationProxy.uploadFile(ExStorageFileManagerImpersonationProxy.java:45)

                            at com.jivesoftware.community.impl.ExStorageBinaryBodyManager.saveBinaryBodyStream_aroundBody4(ExStorageBinaryBodyManager.java:72)

                    Sadly it doesn't look like there's any additional logging I can add on my side to see what it is that we're having a problem reading. If we had a test / non-cloud server I'd want to put a patch in place that gave us some additional information while in this method:

                        private ExStorageErrorEntity getExStorageError(String rawResponse, Header[] headers) {

                            if (StringUtils.isNullOrEmpty(rawResponse)) {

                                return null;

                            }

                     

                     

                            try (InputStream content = IOUtils.toInputStream(rawResponse)) {

                                return ResponseReadingStrategy.GsonReadingStrategy.readFromResponse(content, headers, ExStorageErrorEntity.class);

                            }

                            catch (Exception e) {

                                log.error("Failed to read ExStorageError from response", e);

                                return null;

                            }

                    specifically looking at the value of "rawResponse".  You mentioned in another thread that you're testing this against cloud but that ultimately it'll get deployed against 7.0.x. Do you have a 7.0.x server that you could test this against and apply a change to?

                      • Re: Custom error message from ESP in Jive
                        ztenerowicz

                        Looking at error.getReason() switch I see some of the cases are actually calling error.getMessage().

                        I'd need to know what that method is getting, because in our experiments if we sent the {"reason":"UNKNOWN", "message":"foo"} user would still get the default error message in the UI.

                         

                        There still are some errors for which we don't send the ExStorageError object back, so the error in the log might be because of that.

                         

                        All our testing is done against a single cloud instance which seems to be regularly updated to latest 8.x. AFAIK, we're in talks to get a 7.0.x to work with. For now we were told ExStorage Framework should work the same in both.

                          • Re: Custom error message from ESP in Jive

                            The ESF does not allow you to pass and show to the end user a custom message.

                            It covers most (If not all cases), and shows an error according to it.

                             

                            You should return an error with one of the next reasons:

                                PROVIDER_UNAVAILABLE,

                                PROVIDER_UNAUTHORIZED,

                                UNAUTHORIZED,

                                NOT_FOUND,

                                ILLEGAL_NAME,

                                ILLEGAL_CONTENT_TYPE,

                                ALREADY_EXISTS,

                                QUOTA_EXCEEDED,

                                RESOURCE_OUT_OF_SYNC,

                                LOCKED,

                                RESOURCE_TEMPORARILY_UNAVAILABLE,

                                MISSING_CONFIGURATION,

                                HEALTHCHECK_ERROR,

                                PROVIDER_BUSY,

                                UNKNOWN;

                             

                            Moshe.