All files / module/actions createAction.js

4.55% Statements 1/22
0% Branches 0/19
16.67% Functions 1/6
4.55% Lines 1/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97                                                                                              5x                                                                                                  
import { ActionError } from '../../errors/ActionError'
import { processResponseData } from '../helpers/processResponseData'
import { hasOwn } from '../../shared/utils'
 
/**
 * Do a crude check for common json:api resource properties.
 * This is (probably) not a long-term solution!
 *
 * @param {Object} resourceObject
 * @returns Boolean
 */
export function validateResourceObject (resourceObject) {
  return typeof resourceObject === 'object' &&
    hasOwn(resourceObject, 'type') &&
    hasOwn(resourceObject, 'attributes')
}
 
/**
 * Create a new resource
 *
 * Allowed Responses:
 *
 * _201 Created_
 *
 * If the resource did not have a client-generated id, this is
 * the server response for successful creates. The response body
 * must include the generated resource.
 *
 * If a client-generated id was sent to the server, the response id
 * must match the request id.
 *
 * _202 Accepted_
 *
 * As with the delete action, this library tries to handle accepted as a
 * success state. *If* the resource was sent with a client-generated
 * id, it will be committed to the store as is, however, if no id
 * is present, an error will be thrown.
 *
 * _204 No content_
 *
 * For resources with client-generated ids, a 204 response from the
 * server will commit the provided resource to the store module.
 *
 * @param {ResourcefulApi} api
 * @param {String} moduleName
 */
export function createAction (api, moduleName) {
  return new Proxy(() => {}, {
    apply (target, thisArg, [vuexFns, resourceObject]) {
      if (!validateResourceObject(resourceObject)) {
        throw new Error('Please pass valid json:api resource objects to this action.')
      }
 
      const hasClientGeneratedId = hasOwn(resourceObject, 'id')
 
      vuexFns.commit('startLoading')
 
      // It is currently not supported to pass query params when creating a new resource
      return api[moduleName].create(null, { data: resourceObject }).then(response => {
        if (response.status === 201) {
          // servers are not supposed to alter client-provided ids
          if (hasClientGeneratedId && !hasOwn(response.data.book, resourceObject.id)) {
            // TODO: Throwing here should be configurable, it may be desirable
            //       to be less pedantic.
            throw new ActionError('Server altered client-provided id, with HTTP status 201 instead of 409')
          }
 
          processResponseData(vuexFns, api, moduleName, response.data, 'create')
        }
 
        if (response.status === 202) {
          if (!hasClientGeneratedId) {
            console.warn('Cannot commit expected change because no id is available.')
          } else {
            processResponseData(vuexFns, api, moduleName, response.data, 'create')
          }
        }
 
        if (response.status === 204) {
          if (!hasClientGeneratedId) {
            throw new ActionError('Unexpected empty response from server: missing client-generated id.')
          }
 
          vuexFns.commit('set', { id: resourceObject.id, data: resourceObject.data })
        }
 
        vuexFns.commit('endLoading')
 
        return response
      }).catch(e => {
        // FIXME: This is not the way to handle errors.
        console.log(e)
      })
    }
  })
}