Skip to content

Commit

Permalink
feat(#307): add more CRUD functions (#321)
Browse files Browse the repository at this point in the history
* chore(#28): add DruxtEntityForm example

* feat(#317): add createResource method

* feat(#318): add updateResource method

* chore(#307): update to use CRUD methods

* chore(#307): update tests

* chore(#307): update test coverage
  • Loading branch information
Decipher authored Oct 10, 2021
1 parent ee15810 commit 897dcbc
Show file tree
Hide file tree
Showing 19 changed files with 562 additions and 41 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-pandas-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"druxt": minor
---

Added updateResource method to DruxtClient
5 changes: 5 additions & 0 deletions .changeset/shy-games-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"druxt-entity": minor
---

Updated DruxtEntityForm to use new DruxtClient methods
5 changes: 5 additions & 0 deletions .changeset/ten-students-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"druxt": minor
---

Added createResource method to DruxtClient
9 changes: 9 additions & 0 deletions examples/nuxt/entity-form/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# entity-form

This directory contains an example of the DruxtEntityForm component in Nuxt.

## Setup

```
yarn && yarn dev
```
3 changes: 3 additions & 0 deletions examples/nuxt/entity-form/nuxt.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
buildModules: [['druxt-entity', { baseUrl: process.env.BASE_URL || 'https://demo-api.druxtjs.org' }]]
}
12 changes: 12 additions & 0 deletions examples/nuxt/entity-form/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "entity-form",
"private": true,
"packageManager": "yarn@3.0.0",
"scripts": {
"dev": "nuxt"
},
"dependencies": {
"druxt-site": "link:../../../packages/druxt-entity",
"nuxt": "latest"
}
}
31 changes: 31 additions & 0 deletions examples/nuxt/entity-form/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<div>
<h1>DruxtEntityForm example:</h1>

<h2>Contact form - Basic</h2>
<pre><code>&lt;DruxtEntityForm type="contact_message--feedback" /&gt;</code></pre>

<pre v-if="response">
<code>{{ JSON.stringify(response, null, ' ') }}</code>
</pre>

<DruxtEntityForm
type="contact_message--feedback"
@error="setResponse"
@submit="setResponse"
/>
</div>
</template>

<script>
export default {
data: () => ({
response: null,
}),
methods: {
setResponse(response) {
this.response = response;
},
},
};
</script>
Empty file.
68 changes: 68 additions & 0 deletions packages/druxt/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,41 @@ class DruxtClient {
}
}

/**
* Create a JSON:API resource.
*
* @param {object} resource - The JSON:API resource object
*
* @returns {object} The response data
*/
async createResource(resource) {
if (resource.id) {
return this.updateResource(resource)
}

const { href } = await this.getIndex(resource.type)
if (!href) {
return false
}

let response
try {
response = await this.axios.post(
href,
{ data: resource },
{
headers: {
'Content-Type': 'application/vnd.api+json'
}
}
)
} catch (err) {
response = (err.response || {}).data || err.message
}

return response
}

/**
* Get a collection of resources from the JSON:API server.
*
Expand Down Expand Up @@ -211,6 +246,7 @@ class DruxtClient {
this.index = index.data.links

// Use JSON API resource config to decorate the index.
// @TODO - Add test coverage
if (this.index[this.options.jsonapiResourceConfig]) {
const resources = await this.axios.get(this.index[this.options.jsonapiResourceConfig].href)
for (const resourceType in resources.data.data) {
Expand Down Expand Up @@ -269,6 +305,38 @@ class DruxtClient {
return false
}
}

/**
* Update a JSON:API resource.
*
* @param {object} resource - The JSON:API resource object
*
* @returns {object} The response data
*/
async updateResource(resource) {
const { href } = await this.getIndex(resource.type)
if (!href) {
return false
}
const url = [href, resource.id].join('/')

let response
try {
response = await this.axios.patch(
url,
{ data: resource },
{
headers: {
'Content-Type': 'application/vnd.api+json'
}
}
)
} catch (err) {
response = (err.response || {}).data || err.message
}

return response
}
}

export { DruxtClient }
Expand Down
50 changes: 50 additions & 0 deletions packages/druxt/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,52 @@ describe('DruxtClient', () => {
expect(() => druxt.checkPermissions(res)).toThrow('Some resources have been omitted because of insufficient authorization.\n\n Required permissions: administer node fields.')
})

test('createResource', async () => {
let response
const headers = {
'Content-Type': 'application/vnd.api+json'
}

// Expect invalid resource to return false.
expect((await druxt.createResource({}))).toBe(false)

// Create a Feedback Contact message resource without require data.
const data = { type: 'contact_message--feedback' }
response = await druxt.createResource(data)
expect(mockAxios.post).toHaveBeenCalledWith(
`${baseUrl}/en/jsonapi/contact_message/feedback`,
{ data },
{ headers }
)
expect(response.errors.length).toBe(2)
mockAxios.reset()

// Create resource with required data.
data.attributes = {
subject: 'Subject',
message: 'Test'
}
response = await druxt.createResource(data)
expect(mockAxios.post).toHaveBeenCalledWith(
`${baseUrl}/en/jsonapi/contact_message/feedback`,
{ data },
{ headers }
)
expect(response.errors).toBe(undefined)
expect(response.status).toBe(200)
expect(response.data.data.attributes.subject).toBe(data.attributes.subject)
expect(response.data.data.attributes.message).toBe(data.attributes.message)
mockAxios.reset()

// Update resource.
await druxt.createResource(response.data.data)
expect(mockAxios.patch).toHaveBeenCalledWith(
`${baseUrl}/en/jsonapi/contact_message/feedback/${response.data.data.id}`,
{ data: response.data.data },
{ headers }
)
})

test('getCollection', async () => {
// Get a collection of 'node--page' resources.
const collection = await druxt.getCollection('node--page')
Expand Down Expand Up @@ -135,4 +181,8 @@ describe('DruxtClient', () => {
const empty = await druxt.getResource()
expect(empty).toBe(false)
})

test('updateResource', async () => {
expect((await druxt.updateResource({}))).toBe(false)
})
})
31 changes: 10 additions & 21 deletions packages/entity/src/components/DruxtEntityForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,34 +87,23 @@ export default {
if (this.submitting) return false
this.submitting = true
let url = this.schema.config.href
let method = 'post'
if (this.entity.id) {
// @todo - Reduce size of payload by only sending changed data.
url = [url, this.entity.id].join('/')
method = 'patch'
let method = 'createResource'
if (this.model.id) {
method = 'updateResource'
}
this.response = await this.$druxt[method](this.model)
// Try to send data to backend, and catch any resulting errors.
try {
this.response = await this.$druxt.axios[method](
url,
{ data: this.model },
{
headers: {
'Content-Type': 'application/vnd.api+json',
},
}
)
// Update the Vuex store.
// Handle the response
if (!this.response.errors) {
const resource = this.response.data.data
// Update the Vuex store.
this.$store.commit('druxt/addResource', { resource })
this.$emit('submit', resource)
} catch (e) {
this.response = (e.response || {}).data || e.message
}
else {
this.$emit('error', this.response)
}
this.submitting = false
},
},
Expand Down
23 changes: 12 additions & 11 deletions packages/entity/test/components/DruxtEntityForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ describe('DruxtEntityForm', () => {

// Submit.
await wrapper.find('button#submit').trigger('click')
// TODO : Fix DruxtEntityForm tests.
// expect(wrapper.emitted().error).toBeFalsy()
// expect(wrapper.emitted().submit).toBeTruthy()
// expect(wrapper.vm.response.data.data.id).toBe('8e8d340a-04af-461a-ac63-12415d33e936')
// expect(wrapper.vm.errors).toBe(undefined)
// expect(wrapper.vm.$refs.title.errors.length).toBe(0)
await localVue.nextTick()
expect(wrapper.emitted().error).toBeFalsy()
expect(wrapper.emitted().submit).toBeTruthy()
expect(wrapper.vm.response.data.data.id).toBe('8e8d340a-04af-461a-ac63-12415d33e936')
expect(wrapper.vm.errors).toBe(undefined)
expect(wrapper.vm.$refs.title.errors.length).toBe(0)

// Reset button.
wrapper.find('button#reset').trigger('click')
Expand All @@ -106,12 +106,13 @@ describe('DruxtEntityForm', () => {

// Submit.
await wrapper.find('button#submit').trigger('click')
await localVue.nextTick()
await localVue.nextTick()
expect(wrapper.emitted().error).toBeTruthy()
expect(wrapper.emitted().submit).toBeFalsy()
expect(wrapper.vm.entity.id).toBe(undefined)
// TODO : Fix DruxtEntityForm tests.
// expect(wrapper.vm.errors.length).toBe(1)
// expect(wrapper.vm.$refs.title.errors.length).toBe(1)
expect(wrapper.vm.errors.length).toBe(1)
expect(wrapper.vm.$refs.title.errors.length).toBe(1)

// Reset button.
wrapper.find('button#reset').trigger('click')
Expand All @@ -137,9 +138,9 @@ describe('DruxtEntityForm', () => {

// Submit.
await wrapper.find('button#submit').trigger('click')
await localVue.nextTick()
expect(wrapper.emitted().error).toBeFalsy()
// TODO : Fix DruxtEntityForm tests.
// expect(wrapper.emitted().submit).toBeTruthy()
expect(wrapper.emitted().submit).toBeTruthy()
// expect(wrapper.vm.response.data.data.id).toBe(uuid)
expect(wrapper.vm.errors).toBe(undefined)
expect(wrapper.vm.$refs.title.errors.length).toBe(0)
Expand Down
Loading

0 comments on commit 897dcbc

Please sign in to comment.