ASHdevelopment / standards

ASH Development Standards
28 stars 13 forks source link

Detail error handling transformation #143

Open ASH-Bryan opened 6 years ago

ASH-Bryan commented 6 years ago

Although we state in standards that validation error responses should conform to API standard, it does not appear that we are doing this on the back end. Our existing applications have adapter logic to reformat service stack error responses.

Is this the best approach?

If so, we need to agree on common adapter logic to handle this. Ideally it should be an addon.

ASH-Bryan commented 6 years ago

Examples of some different ways this is being done currently:

    handleResponse(status, headers, payload, requestData) {
        if (isPresent(payload.responseStatus)) {
            const serverError = payload.responseStatus;
            const message = serverError.message;

            serverError.errors.push({
                detail: message,
            });

            return new InvalidError(serverError.errors);
        }

        return this._super(status, headers, payload, requestData);
    }
    handleResponse(status, headers, payload) {
        if (status === 400) {
            let message;

            try {
                message = { "detail": payload.responseStatus.message };
            } catch (error) {
                message = { "detail": this._getDefaultMessage() };

            } finally {
                payload.errors = [message];
            }
            return payload;
        }

        return this._super(...arguments);
    }
    handleResponse(status, headers, payload, requestData) {
        if (this.isInvalid(status)) {
            payload.errors = [{
                detail: payload.responseStatus.message,
                code: payload.responseStatus.errorCode
            }];
        }
        return this._super(status, headers, payload, requestData);
    }
    handleResponse(status, headers, payload, requestData) {
        let newPayload = payload;

        if (isPresent(payload.responseStatus)) {
            newPayload = payload.responseStatus;

            newPayload.errors = [
                {
                    detail: newPayload.message,
                    source: {
                        pointer: `data/attributes/${dasherize(newPayload.errorCode)}`
                    }
                }
            ]

            return new InvalidError(newPayload.errors);
        }

        return this._super(status, headers, newPayload, requestData);
    }
    handleResponse(status, headers, payload, requestData) {
        if (status === 400 && payload.responseStatus) {
            payload = payload.responseStatus;
            payload.errors = [{ detail: payload.message }];

            return new InvalidError(payload.errors);
        }

        return this._super(status, headers, payload, requestData);
    }
    handleResponse(status, headers, payload, requestData) {
        if ((status === 400 || status === 500) && payload.responseStatus) {
            const response = payload.responseStatus;

            if(!isPresent(response.errors)) {
                response.errors = [];
            }

            if(isPresent(response.message)) {
                response.errors.push({
                    detail: response.message
                });
            }
            return new InvalidError(response.errors);
        }

        return this._super(status, headers, payload, requestData);
    }
ASH-Bryan commented 6 years ago

Also let's decide on status codes - I believe that 422 is the only officially recognized status code for field validation errors.

ASH-Bryan commented 6 years ago

For reference, here is the 'approved' service stack error format:

"responseStatus": {
    "errorCode": "ArgumentException",
    "message": "Invalid Request",
    "stackTrace": "redacted",
    "errors": [
        {
            "errorCode": "InValid",
            "fieldName": "CurrentPassword",
            "message": "Oops! Password is incorrect."
        }
    ],
    "meta": null
}

And here is the adapter method I wrote which will also give us access to the field-level errors. The above functions will only show top-level error messages. We could also branch this on the status code if we get back end to adhere to Ember's 422 code for validations:

    handleResponse(status, headers, payload) {
        if (status >= 400 && payload.responseStatus) {
            const errors = [];
            if (isEmpty(payload.responseStatus.errors)) {
                //Top level error message only
                errors.push(payload.responseStatus.message);
            } else {
                //Iterate through the field validations
                payload.responseStatus.errors
                    .forEach(error => errors.push({
                        detail: error.message,
                        source: { pointer: error.fieldName }
                    }));
            }
            return new InvalidError(errors);
        }
        return this._super(...arguments);
    }
ASH-Michael commented 6 years ago

Team agrees to table this standard until CRAFT and Front End team meeting discussions take place to determine which approach to take.

ASH-Michael commented 5 years ago

Table until after addon with adapter mixin is created to handle error responses.