Commit b594652a authored by Jay Huskins's avatar Jay Huskins Committed by Coby Sher
Browse files

Resolve #3280002 - Changes to parameters should invalidate state

parent 35453935
Loading
Loading
Loading
Loading
Loading
+30 −11
Original line number Diff line number Diff line
@@ -486,19 +486,38 @@ class DrupalState {
    all = false,
    refresh = false,
  }: GetObjectParams): Promise<PartialState<State> | void> {
    if (
      params !== undefined &&
      typeof params !== 'string' &&
      !(params instanceof DrupalJsonApiParams)
    ) {
      this.onError(
        new Error(
          `Invalid params: Params must be a string or instance of DrupalJsonApiParams (https://www.npmjs.com/package/drupal-jsonapi-params)`
        )
      );
      return;
    }
    const state = this.getState() as DsState;
    const paramString =
      typeof params === 'string' ? params : params?.getQueryString();
    const collectionKey = paramString
      ? `${objectName}-${paramString}`
      : objectName;
    const resourceKey = `${objectName}Resources`;
    // Check for collection in the store
    const collectionState = state[objectName] as TJsonApiBodyDataRequired;
    const collectionState = state[collectionKey] as TJsonApiBodyDataRequired;

    // If an id is provided, find and return a resource
    if (id) {
      const resourceId = paramString ? `${id}-${paramString}` : id;
      const resourceState = !refresh
        ? (state[`${objectName}Resources`] as keyedResources)
        ? (state[resourceKey] as keyedResources)
        : false;

      // If requested resource is in the resource store, return that
      if (resourceState) {
        const resource = resourceState[id] as keyedResources;
        const resource = resourceState[resourceId] as keyedResources;
        if (resource) {
          !this.debug || console.log(`Matched resource ${id} in state`);
          return resource?.graphql
@@ -559,24 +578,24 @@ class DrupalState {
        res
      )) as keyedResources;

      const objectResourceState = state[`${objectName}Resources`];
      const objectResourceState = state[resourceKey];

      if (objectResourceState) {
        // If the resource state exists, add the new resource to it.
        const updatedResourceState = {
          ...objectResourceState,
          [id]: resourceData,
          [resourceId]: resourceData,
        };

        this.setState({
          [`${objectName}Resources`]: updatedResourceState,
          [resourceKey]: updatedResourceState,
        });
      } else {
        const newResourceState = {
          [id]: resourceData,
          [resourceId]: resourceData,
        };

        this.setState({ [`${objectName}Resources`]: newResourceState });
        this.setState({ [resourceKey]: newResourceState });
      }

      return query
@@ -630,7 +649,7 @@ class DrupalState {
      )) as keyedResources;

      const fetchedCollectionState = {} as CollectionState;
      fetchedCollectionState[objectName] = collectionData;
      fetchedCollectionState[collectionKey] = collectionData;

      this.setState(fetchedCollectionState);
      // if the all flag is present
@@ -684,11 +703,11 @@ class DrupalState {
            const currentState = this.getState() as CollectionState;
            // using deepmerge to merge arrays instead of overwriting them
            const mergedCollection: keyedResources = deepmerge(
              currentState[objectName],
              currentState[collectionKey],
              nextPage
            );

            currentState[objectName] = mergedCollection;
            currentState[collectionKey] = mergedCollection;
            this.setState(currentState);

            return nextPage.links as TJsonApiLinks;
+64 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ global.Headers = fetchMock.Headers;

import { ServerResponse } from 'http';
import fetch from 'isomorphic-fetch';
import { DrupalJsonApiParams } from 'drupal-jsonapi-params';

import DrupalState from '../DrupalState';

@@ -153,6 +154,38 @@ describe('drupalState', () => {
    expect(fetchMock).toBeCalledTimes(2);
  });

  test('Re-fetch resource if it exists in state but uses different parameters', async () => {
    const store: DrupalState = new DrupalState({
      apiBase: 'https://dev-ds-demo.pantheonsite.io',
      apiPrefix: 'jsonapi',
      debug: true,
    });
    store.setState({ dsApiIndex: indexResponse.links });
    fetchMock.mock(
      'https://dev-ds-demo.pantheonsite.io/en/jsonapi/node/recipe/33386d32-a87c-44b9-b66b-3dd0bfc38dca?filter%5Bstatus%5D=1',
      {
        status: 200,
        body: recipesResourceData1,
      }
    );
    expect(
      await store.getObject({
        objectName: 'node--recipe',
        id: '33386d32-a87c-44b9-b66b-3dd0bfc38dca',
      })
    ).toEqual(recipesResourceObject1);
    const params = new DrupalJsonApiParams();
    params.addFilter('status', '1');
    expect(
      await store.getObject({
        objectName: 'node--recipe',
        id: '33386d32-a87c-44b9-b66b-3dd0bfc38dca',
        params,
      })
    ).toEqual(recipesResourceObject1);
    expect(fetchMock).toBeCalledTimes(2);
  });

  test('Add resource object to local resource state if resource state already exists', async () => {
    const store: DrupalState = new DrupalState({
      apiBase: 'https://dev-ds-demo.pantheonsite.io',
@@ -298,6 +331,37 @@ describe('drupalState', () => {
    expect(fetchMock).toBeCalledTimes(1);
  });

  test('Re-fetch object if it exists in state but uses different parameters', async () => {
    const store: DrupalState = new DrupalState({
      apiBase: 'https://dev-ds-demo.pantheonsite.io',
      apiPrefix: 'jsonapi',
      debug: true,
    });
    store.setState({ dsApiIndex: indexResponse.links });
    fetchMock.mock(
      'https://dev-ds-demo.pantheonsite.io/en/jsonapi/node/recipe?filter%5Bstatus%5D=1',
      {
        status: 200,
        body: recipes,
      },
      { overwriteRoutes: true }
    );
    expect(
      await store.getObject({
        objectName: 'node--recipe',
      })
    ).toEqual(recipesCollectionObject1);
    const params = new DrupalJsonApiParams();
    params.addFilter('status', '1');
    expect(
      await store.getObject({
        objectName: 'node--recipe',
        params,
      })
    ).toEqual(recipesCollectionObject1);
    expect(fetchMock).toBeCalledTimes(2);
  });

  test('Fetch resource with authentication', async () => {
    const store: DrupalState = new DrupalState({
      apiBase: 'https://dev-ds-demo.pantheonsite.io',