BackendSrv: Fixes queue countdown when unsubscribe is before response (#28323)

This commit is contained in:
Hugo Häggmark 2020-10-16 16:20:58 +02:00 committed by GitHub
parent 5036c87540
commit 9305117902
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 13 additions and 22 deletions

View File

@ -26,30 +26,28 @@ const getTestContext = () => {
const fetchMock = jest.fn().mockReturnValue(fetchResult);
const setInProgressMock = jest.fn();
const setDoneMock = jest.fn();
const queueMock: FetchQueue = ({
add: jest.fn(),
setInProgress: setInProgressMock,
setDone: setDoneMock,
setDone: jest.fn(),
getUpdates: jest.fn(),
} as unknown) as FetchQueue;
const responseQueue = new ResponseQueue(queueMock, fetchMock);
return { id, options, expects, fetchMock, setInProgressMock, setDoneMock, responseQueue, fetchResult };
return { id, options, expects, fetchMock, setInProgressMock, responseQueue, fetchResult };
};
describe('ResponseQueue', () => {
describe('add', () => {
describe('when called', () => {
it('then the matching fetchQueue entry should be set to inProgress', () => {
const { id, options, setInProgressMock, setDoneMock, responseQueue } = getTestContext();
const { id, options, setInProgressMock, responseQueue } = getTestContext();
responseQueue.add(id, options);
expect(setInProgressMock.mock.calls).toEqual([['id']]);
expect(setDoneMock).not.toHaveBeenCalled();
});
it('then a response entry with correct id should be published', done => {
@ -81,14 +79,13 @@ describe('ResponseQueue', () => {
describe('and when the fetch Observable is completed', () => {
it('then the matching fetchQueue entry should be set to Done', done => {
const { id, options, responseQueue, setInProgressMock, setDoneMock } = getTestContext();
const { id, options, responseQueue, setInProgressMock } = getTestContext();
subscribeTester({
observable: responseQueue.getResponses(id).pipe(first()),
expectCallback: data => {
data.observable.subscribe().unsubscribe();
expect(setInProgressMock.mock.calls).toEqual([['id']]);
expect(setDoneMock.mock.calls).toEqual([['id']]);
},
doneCallback: done,
});

View File

@ -1,5 +1,5 @@
import { Observable, Subject } from 'rxjs';
import { filter, finalize } from 'rxjs/operators';
import { filter } from 'rxjs/operators';
import { BackendSrvRequest, FetchResponse } from '@grafana/runtime';
import { FetchQueue } from './FetchQueue';
@ -27,17 +27,7 @@ export class ResponseQueue {
// Let the fetchQueue know that this id has started data fetching.
fetchQueue.setInProgress(id);
this.responses.next({
id,
observable: fetch(options).pipe(
// finalize is called whenever this observable is unsubscribed/errored/completed/canceled
// https://rxjs.dev/api/operators/finalize
finalize(() => {
// Let the fetchQueue know that this id is done.
fetchQueue.setDone(id);
})
),
});
this.responses.next({ id, observable: fetch(options) });
});
}

View File

@ -65,10 +65,11 @@ export class BackendSrv implements BackendService {
}
fetch<T>(options: BackendSrvRequest): Observable<FetchResponse<T>> {
return new Observable(observer => {
// We need to match an entry added to the queue stream with the entry that is eventually added to the response stream
const id = uuidv4();
// We need to match an entry added to the queue stream with the entry that is eventually added to the response stream
const id = uuidv4();
const fetchQueue = this.fetchQueue;
return new Observable(observer => {
// Subscription is an object that is returned whenever you subscribe to an Observable.
// You can also use it as a container of many subscriptions and when it is unsubscribed all subscriptions within are also unsubscribed.
const subscriptions: Subscription = new Subscription();
@ -89,6 +90,9 @@ export class BackendSrv implements BackendService {
// This returned function will be called whenever the returned Observable from the fetch<T> function is unsubscribed/errored/completed/canceled.
return function unsubscribe() {
// Change status to Done moved here from ResponseQueue because this unsubscribe was called before the responseQueue produced a result
fetchQueue.setDone(id);
// When subscriptions is unsubscribed all the implicitly added subscriptions above are also unsubscribed.
subscriptions.unsubscribe();
};