Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Spying link #71

Open
tomitrescak opened this issue Sep 12, 2017 · 2 comments
Open

Spying link #71

tomitrescak opened this issue Sep 12, 2017 · 2 comments

Comments

@tomitrescak
Copy link

tomitrescak commented Sep 12, 2017

Hi, I'm trying to write a link which will let the client know how many running queries are still running (for testing). I wrote following on, but the issue there is that if I run n consecutive queries of a same name but with different parameters, only the last query will receive "complete" callback. Any idea what is going on? I used your "Dedupe" link as a base for this

import {
  ApolloLink,
  FetchResult,
  NextLink,
  Observable,
  Operation,
} from 'apollo-link-core';

import ApolloClient from 'apollo-client';
import { print } from 'graphql/language/printer';

/*
 * Expects context to contain the forceFetch field if no dedup
 */
export class SpyLink extends ApolloLink {
  private client: () => ApolloClient;

  private inFlightRequestObservables: {
    [key: string]: Observable<FetchResult>;
  };

  constructor(client: () => ApolloClient) {
    super();
    this.inFlightRequestObservables = {};
    this.client = client;
  }

  /** Will wait for all queries to finish running */
  public async wait() {
    return new Promise((resolve) => {
      const check = () => {
        if (Object.getOwnPropertyNames(this.inFlightRequestObservables).length === 0) {
          resolve();
        } else {
          setTimeout(check, 5);
        }
      };
      check();
    });
  }

  public request(
    operation: Operation,
    forward: NextLink,
  ): Observable<FetchResult> {
    // we need to add it like this, brutally as we have no access ton client on individual links
    if (!(this.client() as any).spyLink) {
      (this.client() as any).spyLink = this;
    }

    const key = this.getKey(operation);
    if (!this.inFlightRequestObservables[key]) {
      this.inFlightRequestObservables[key] = forward(operation);
    }
    return new Observable<FetchResult>((observer) =>
      this.inFlightRequestObservables[key].subscribe({
        next: observer.next.bind(observer),
        error: (error) => {
          delete this.inFlightRequestObservables[key];
          observer.error(error);
        },
        complete: () => {
          delete this.inFlightRequestObservables[key];
          observer.complete();
        },
      }),
    );
  }

  private getKey(operation: Operation) {
    return `${print(operation.query)}|${JSON.stringify(
      operation.variables,
    )}|${operation.operationName}`;
  }
}
@tomitrescak
Copy link
Author

Just so you know, changing

private getKey(operation: Operation) {
    return `${print(operation.query)}|${JSON.stringify(
      operation.variables,
    )}|${operation.operationName}`;
  }

to

private getKey(operation: Operation) {
    return `${print(operation.query)}`;
  }

Solves the problem obviously as only the query itself is now the key. Works ... as long as queries are returned in the same order as requested ... which in a lot of cases will NOT.

Any help is very much appreciated.

@fbartho
Copy link
Contributor

fbartho commented Oct 26, 2017

Is operations.variables an array @tomitrescak? otherwise your JSON.stringify is dependent on the order of entries in a hash, which I don't think is guaranteed.

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

No branches or pull requests

3 participants