diff --git a/.changeset/few-pets-suffer.md b/.changeset/few-pets-suffer.md new file mode 100644 index 000000000..9f5cee8bb --- /dev/null +++ b/.changeset/few-pets-suffer.md @@ -0,0 +1,6 @@ +--- +"druxt": minor +--- + +feat(#639): added druxt/flushCollection and druxt/flushResource mutations +feat(#639): added bypassCache option to druxt/getCollection and druxt/getResource actions diff --git a/packages/druxt/src/stores/druxt.js b/packages/druxt/src/stores/druxt.js index 5218956b8..5365e3595 100644 --- a/packages/druxt/src/stores/druxt.js +++ b/packages/druxt/src/stores/druxt.js @@ -140,6 +140,44 @@ const DruxtStore = ({ store }) => { Vue.set(state.resources[type][id], prefix, resource) }, + + /** + * @name flushCollection + * @mutator {object} flushCollection=collections Removes JSON:API collections from the Vuex state object. + * @param {flushCollectionContext} context + * + * @example @lang js + * // Flush all collections. + * this.$store.commit('druxt/flushCollection', {}) + * + * // Flush target collection. + * this.$store.commit('druxt/flushCollection', { type, hash, prefix }) + */ + flushCollection (state, { type, hash, prefix }) { + if (!type) Vue.set(state, 'collections', {}) + else if (type && !hash && !prefix) Vue.set(state.collections, type, {}) + else if (type && hash && !prefix) Vue.set(state.collections[type], hash, {}) + else if (type && hash && prefix) Vue.set(state.collections[type][hash], prefix, {}) + }, + + /** + * @name flushResource + * @mutator {object} flushResource=resources Removes JSON:API resources from the Vuex state object. + * @param {flushResourceContext} context + * + * @example @lang js + * // Flush all resources. + * this.$store.commit('druxt/flushResources', {}) + * + * // Flush target resource. + * this.$store.commit('druxt/flushResources', { id, type, prefix, hash }) + */ + flushResource (state, { type, id, prefix }) { + if (!type) Vue.set(state, 'resources', {}) + else if (type && !id && !prefix) Vue.set(state.resources, type, {}) + else if (type && id && !prefix) Vue.set(state.resources[type], id, {}) + else if (type && id && prefix) Vue.set(state.resources[type][id], prefix, {}) + } }, /** @@ -159,15 +197,16 @@ const DruxtStore = ({ store }) => { * const resources = await this.$store.dispatch('druxt/getCollection', { * type: 'node--article', * query: new DrupalJsonApiParams().addFilter('status', '1'), + * bypassCache: false * }) */ - async getCollection ({ commit, state }, { type, query, prefix }) { + async getCollection ({ commit, state }, { type, query, prefix, bypassCache = false }) { // Generate a hash using query data excluding the 'fields' and 'include' data. const queryObject = getDrupalJsonApiParams(query).getQueryObject() const hash = query ? md5(JSON.stringify({ ...queryObject, fields: {}, include: [] })) : '_default' // If collection hash exists, re-hydrate and return the data. - if (((state.collections[type] || {})[hash] || {})[prefix]) { + if (!bypassCache && ((state.collections[type] || {})[hash] || {})[prefix]) { return { ...state.collections[type][hash][prefix], // Hydrate resource data. @@ -197,11 +236,15 @@ const DruxtStore = ({ store }) => { * @return {object} The Drupal JSON:API resource. * * @example @lang js - * const resource = await this.$store.dispatch('druxt/getResource', { type: 'node--article', id }) + * const resource = await this.$store.dispatch('druxt/getResource', { + * type: 'node--article', + * id, + * bypassCache: false + * }) */ - async getResource ({ commit, dispatch, state }, { type, id, query, prefix }) { + async getResource ({ commit, dispatch, state }, { type, id, query, prefix, bypassCache = false }) { // Get the resource from the store if it's avaialble. - const storedResource = ((state.resources[type] || {})[id] || {})[prefix] ? + const storedResource = !bypassCache && ((state.resources[type] || {})[id] || {})[prefix] ? { ...state.resources[type][id][prefix] } : null @@ -351,6 +394,40 @@ export { DruxtStore } * } */ +/** + * Parameters for the `flushCollection` mutation. + * + * @typedef {object} flushCollectionContext + * + * @param {string} type - The JSON:API collection resource type. + * @param {string} hash - An md5 hash of the query string. + * @param {string} [prefix] - (Optional) The JSON:API endpoint prefix or langcode. + * + * @example @lang js + * { + * type: 'node--page', + * hash: '_default', + * prefix: 'en' + * } + */ + +/** + * Parameters for the `flushResource` mutation. + * + * @typedef {object} flushResourceContext + * + * @param {string} [type] - The JSON:API Resource type. + * @param {string} [id] - The Drupal resource UUID. + * @param {string} [prefix] - (Optional) The JSON:API endpoint prefix or langcode. + * + * @example @lang js + * { + * type: 'node--page', + * id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd', + * prefix: 'en' + * } + */ + /** * Parameters for the `getCollection` action. * @@ -359,11 +436,13 @@ export { DruxtStore } * @param {string} type - The JSON:API collection resource type. * @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. * @param {string} [prefix] - (Optional) The JSON:API endpoint prefix or langcode. + * @param {boolean} [bypassCache] - (Optional) Bypass the Vuex cached collection. * * @example @lang js * { * type: 'node--page', - * query: new DrupalJsonApiParams().addFilter('status', '1') + * query: new DrupalJsonApiParams().addFilter('status', '1'), + * bypassCache: false * } */ @@ -376,12 +455,14 @@ export { DruxtStore } * @param {string} id - The Drupal resource UUID. * @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. * @param {string} [prefix] - (Optional) The JSON:API endpoint prefix or langcode. + * @param {boolean} [bypassCache] - (Optional) Bypass the Vuex cached resource. * * @example @lang js * { * type: 'node--page', * id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd', - * prefix: 'en' + * prefix: 'en', + * bypassCache: false * } */ diff --git a/packages/druxt/test/stores/druxt.test.js b/packages/druxt/test/stores/druxt.test.js index eaa84004e..827805cf2 100644 --- a/packages/druxt/test/stores/druxt.test.js +++ b/packages/druxt/test/stores/druxt.test.js @@ -282,4 +282,51 @@ describe('DruxtStore', () => { await store.dispatch('druxt/getCollection', { type: 'node--page', query: {} }) expect(mockAxios.get).toHaveBeenCalledTimes(2) }) + + test('flushCollection', async () => { + const type = 'node--page' + const hash ='_default' + const prefix = 'en' + + // Ensure that the results state is populated. + const collection = await getMockCollection(type) + store.commit('druxt/addCollection', { collection, type, prefix, hash }) + expect(store.state.druxt.collections[type][hash][prefix]).toStrictEqual(collection) + + store.commit('druxt/flushCollection', { type, hash, prefix }) + expect(store.state.druxt.collections[type][hash][prefix]).toStrictEqual({}) + + store.commit('druxt/flushCollection', { type, hash }) + expect(store.state.druxt.collections[type][hash]).toStrictEqual({}) + + store.commit('druxt/flushCollection', { type }) + expect(store.state.druxt.collections[type]).toStrictEqual({}) + + store.commit('druxt/flushCollection', {}) + expect(store.state.druxt.collections).toStrictEqual({}) + + }) + + test('flushResource', async () => { + const type = 'node--page' + const prefix = 'en' + + // Ensure that the results state is populated. + const resource = await getMockResource(type) + const id = resource.data.id + store.commit('druxt/addResource', { prefix, resource }) + expect(store.state.druxt.resources[type][id][prefix]).toStrictEqual(resource) + + store.commit('druxt/flushResource', { type, id, prefix }) + expect(store.state.druxt.resources[type][id][prefix]).toStrictEqual({}) + + store.commit('druxt/flushResource', { type, id }) + expect(store.state.druxt.resources[type][id]).toStrictEqual({}) + + store.commit('druxt/flushResource', { type }) + expect(store.state.druxt.resources[type]).toStrictEqual({}) + + store.commit('druxt/flushResource', {}) + expect(store.state.druxt.resources).toStrictEqual({}) + }) })