Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api down management #12

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

geniusit
Copy link

I don't use the same linter than you. Sorry. It would be nice to find a way to lint in the same way than you.

@geniusit
Copy link
Author

I have some questions about the application.
First, why the onResponse is always undefined ? I removed it ...
Second, how the handleError is called? I don't find any link in the code... I want to understand it.
Third, I have some health checks needs and I don't want if it's beter to do PR to your official lib or is it beter to make the lib to life of my fork ...

My backend API uses https://quarkus.io/guides/microprofile-health

May be we can have a chat about that ?

Thank you

@geniusit
Copy link
Author

geniusit commented Apr 30, 2020

So I ve understand the whole mecanism. I noticed that you pass undefined as the fourth parameter of the begin function.

@katiawheeler
Copy link
Owner

@geniusit sorry for the delayed response!

Yes, the initial implementation was missing a handling for when the original API went back up - it should have an onResponse handler passed as the fourth parameter of begin. Checking over your code now!

Copy link
Owner

@katiawheeler katiawheeler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let me know if you have any questions/concerns on the edits or if I can explain anything further!

src/pages/StatusPage/index.tsx Outdated Show resolved Hide resolved
src/pages/StatusPage/index.tsx Outdated Show resolved Hide resolved
public handleResponse = (api: Api, response: Response) => {
console.log('handleResponse' + response);
const apis = [...this.state.apis];
const index = apis.findIndex(stateApi => stateApi.api === api);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object equality in JS won't work here - has to check a property or it will always be false.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I my implementation I did it like this :

const index = apis.findIndex(stateApi => stateApi.api.endpoint === api.endpoint);

Should I change here also ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const index = apis.findIndex(stateApi => stateApi.api === api);

is the way that you do...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was probably an oversight on my part - it should probably be updated everywhere. I shall note where to update to make sure we get them all 😄


this.setState({
apis,
hasError: hasError,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just pass hasError. JS/TS will automatically map it.

Suggested change
hasError: hasError,
hasError,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, didn't know it ...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasError: false,
});

await begin(this.props.apis, this.handleResponse, this.props.interval);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we still need the handleError and the handleResponse separately.

handleError will get called specifically if there is an error.
handleResponse will get called if there's not an error (the API is not down or if it came back up).

The logic needs to be:

handleError - set error state
handleResponse - check if previous error state - if there is, iterate through and see if that API is resolved. If no previous error state, do nothing (it's a good response, nothing to update).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the last line on the table: https://github.com/katiawheeler/healthy#functions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I did some change.
According to your impl Im agree that we still need a way to differentiate Response and Error.
I rename Response with Success.
I propose you to have the handler like this :

  1. handleSuccess -> onSuccess
  2. handleError -> onError
  3. handleDown -> onDown

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename onDown to onApiDown just so it's clear what is down?

src/pages/StatusPage/index.tsx Outdated Show resolved Hide resolved
Comment on lines 3 to 36
/** Begins the chain of API calls */
export const begin = async (
apis: Api[],
onError: Handler,
interval?: number,
onResponse?: Handler
) => {
const currentInterval = interval ? interval : 30000;

apis.forEach(async a => {
await makeCall(a, onError, onResponse || undefined);
});

/** Set the timeout and do it again */
setTimeout(
async () => await begin(apis, onError, currentInterval, onResponse || undefined),
currentInterval
);
export const begin = async (apis: Api[], onResponse: Handler, interval?: number) => {
const currentInterval = interval ? interval : 30000;

apis.forEach(async a => {
await makeCall(a, onResponse);
});

/** Set the timeout and do it again */
setTimeout(async () => await begin(apis, onResponse, currentInterval), currentInterval);
};

/** Make the actual fetch and return a Response object */
const makeCall = async (api: Api, onError: Handler, onResponse?: Handler) => {
await fetch(api.endpoint)
.then(apiResponse => handleResponse(api, apiResponse, onError, onResponse || undefined))
// tslint:disable-next-line
.catch(error => console.log('unhandled', error));
const makeCall = async (api: Api, onResponse: Handler) => {
await fetch(api.endpoint)
.then(apiResponse => handleResponse(api, apiResponse, onResponse))
// tslint:disable-next-line
.catch(error => handleResponse(api, error, onResponse));
};

/**
* Handles the Response
*/
const handleResponse = (api: Api, response: Response, onError: Handler, onResponse?: Handler) => {
const apiResponse: ApiResponse = {
code: response.status,
message: response.statusText,
};

if (!response.ok) {
onError(api, apiResponse);
return;
}

if (onResponse) {
onResponse(api, apiResponse);
}
* Handles the Response
*/
const handleResponse = (api: Api, response: Response, onResponse: Handler) => {
const apiResponse: ApiResponse = {
code: response.status,
message: response.statusText,
};

onResponse(api, apiResponse);
};

export default { begin, makeCall, handleResponse };
export default { begin, makeCall, handleResponse };
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully reworked it according to your main idea

@katiawheeler
Copy link
Owner

Also, @geniusit important to note - I am almost done refactoring this library to utilize hooks. I can incorporate your changes in that refactor, or if you'd like I can finish the refactor and you can make the changes using hooks. Whatever you'd like to do I'm fine with 😄

@katiawheeler
Copy link
Owner

@geniusit just checking in that you saw my previous comment and didn't need any further help!

@geniusit
Copy link
Author

geniusit commented May 4, 2020

@katiawheeler just workin on it ! Send you message in the day.
I did a really nice job, I'll send you message in a few hours

@geniusit
Copy link
Author

geniusit commented May 4, 2020

Here is the final result of my HealthCheck :

1
2
3
4

I use https://quarkus.io/guides/microprofile-health

I got this kind of response from my backend :

{
    "status": "DOWN",
    "checks": [
        {
            "name": "Micro Service - Crossing series ",
            "status": "UP"
        },
        {
            "name": "Micro Service - Crossing Scheduler",
            "status": "UP"
        },
        {
            "name": "Micro Service - Vehicle Regex",
            "status": "DOWN",
            "data": {
                "Message": "RESTEASY004655: Unable to invoke request: java.net.ConnectException: Connection refused: connect"
            }
        },
        {
            "name": "Kafka cluster",
            "status": "UP"
        }
    ]
}

It's my first React project and Im very happy of the result. Learning React with react-healthy is perfect.
The question is : when to call onError() and when to call onResponse() ?
Does onError means 500, 503, ... or does onError means that service is Down ?
What does onError means ?

Here is my onBegin signature :

export const begin = async (apis: ApiWithResponse[], onResponse: Handler, interval?: number) => {

In my onResponse Handler I manage everything. Im looking for a nicer way to do it.

I changed to fetch like this :

await fetch(api.api.endpoint)
   .then(apiResponse => handleResponse(api, apiResponse, onResponse))
   // tslint:disable-next-line
   .catch(error => handleError(api, onResponse));
};

BTW I added material-ui. I like it ;)
With that component : https://material-ui.com/components/expansion-panels/

I don't know hooks. I will learn it. I don't know the best way to use react-healthy. I copy/paste some files of your project into mine.
For now I continue to use react-health only to fetch the data ... Is is worth to continue to use react-healthy or copy everything into my project. That the question ... :)

@geniusit
Copy link
Author

geniusit commented May 4, 2020

I did a new commit. Some things to say :
I've tested with StatusPage component and it works well.
However with Healthy component I got the following error :

Uncaught (in promise) TypeError: onDown is not a function
at handleDown (index.js:347)
at index.js:316

I will look on it. Im not yet familiar with the Healthy component.

Also, in Handler is change the signature. The Response is now optional (onDown management)
I take this choice instead of creating two separate Handler.
But it implies to add strictNullCheck in tslint.json.

Also in my impl I add a way to parse the json returned by my api call.
Like this :

// Examine the text in the response
  response.json().then(function(data) {

    const apiResponse: ApiResponse = {
      code: response.status,
      message: response.statusText,
      body: data,
    };

    200 == apiResponse.code ? onSuccess(api, apiResponse) : onError(api, apiResponse);

  });

Here is my Response object definition :

export interface Response {
  /** HTTP Error Code */
  code: number;
  /** Status message */
  message: string;
  /** Body */
  body: HealthCheck;
}

export interface HealthCheck {
  status: string;
  checks: Check [];
}

interface Check {
  name: string;
  status: string;
}

export interface ApiWithResponse {
  api: Api;
  response: Response;
}

Do you want to implement it in your lib. Notice that it's tight coupling with quarkus health check and this is why I think we shouldn't implement it in the react-healthy lib.

Now we have a clear separation of the 3 scenarios : success / failure / down.

I hope that it doesn't break the Healthy component too much.
I will try to look on it asap.

You decide how we merge my code in the master branch. Im very interested by React hooks :)

};
public handleError = (api: Api, response: Response) => {
const apis = [...this.state.apis];
const index = apis.findIndex(stateApi => stateApi.api === api);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should check api.endpoint equality

public componentDidMount = async () => {
const apis = this.props.apis.map(api => ({ api, response: null, hasError: false }));
const apis = [...this.state.apis];
const index = apis.findIndex(stateApi => stateApi.api === api);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should check api.endpoint equality

apis,
hasError: false,
hasError: hasError,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can shorten to hasError,

this.setState({
...this.state,
apis,
hasError: hasError,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can shorten to hasError,

@katiawheeler
Copy link
Owner

So, it looks like your onDown essentially replaces the onError - my initial thinking behind onError is that if the call fails in some way (a typical HTTP Error being thrown) or if the response doesn't match our "success" criteria (your code == 200, etc. checks), it would throw the onError to alert the user that the API is down or unreachable. I am fine renaming it to onApiDown if that seems to make more sense, but I don't necessarily think we need both of those functions.

@katiawheeler
Copy link
Owner

Love the material design, by the way! Very slick!

@katiawheeler
Copy link
Owner

Also, I'll try to pull your branch either today or tomorrow and make sure that everything looks smoothly. Then I'll go ahead and merge it in and update the hooks refactor.

To answer your question of whether or not to implement your HealthCheck work into this lib, I think a better way to handle it would be to modularize the response type and open it up to allow users (yourself included) to pass their own custom response by implementing a base interface. I'll also take a stab at this when I refactor. I think this would be a good thing to have.

@geniusit geniusit force-pushed the enhancement/api_down_management branch from cb684ce to 5e57f15 Compare May 11, 2020 11:11
@geniusit
Copy link
Author

So, it looks like your onDown essentially replaces the onError - my initial thinking behind onError is that if the call fails in some way (a typical HTTP Error being thrown) or if the response doesn't match our "success" criteria (your code == 200, etc. checks), it would throw the onError to alert the user that the API is down or unreachable. I am fine renaming it to onApiDown if that seems to make more sense, but I don't necessarily think we need both of those functions.

@geniusit
Copy link
Author

Also, I'll try to pull your branch either today or tomorrow and make sure that everything looks smoothly. Then I'll go ahead and merge it in and update the hooks refactor.

To answer your question of whether or not to implement your HealthCheck work into this lib, I think a better way to handle it would be to modularize the response type and open it up to allow users (yourself included) to pass their own custom response by implementing a base interface. I'll also take a stab at this when I refactor. I think this would be a good thing to have.

@geniusit geniusit closed this May 11, 2020
@geniusit geniusit reopened this May 11, 2020
@geniusit
Copy link
Author

So I ve removed onApiDown handler. Indeed it makes the same than onError handler.
Basically I wanted to differentiate both onError / onApiDown because having only one handler implies this :

  • the response of the handleResponse become optional parameter :
const handleResponse = (api: Api, onSuccess: Handler, onError: Handler, response?: Response) => {
  • a if statement has to be made in the handleResponse :
if(response){ ...
  • in the handleResponse (consumer) a ternary condition :
response != null ? apis[index].response = response : apis[index].response = null;

I let you decide what you prefer. Me I prefer to have 3 separates Handler because it evics to have if statement in the handleResponse... What do you think ?

@geniusit
Copy link
Author

BTW the Healthy component is still broke... I got the "We are currently experiencing issues with our Api1 service" message even if the API is up. Please could you check?
Thank you !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants