Skip to content

feat(model): add support to configuration at query builder #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 9, 2020
16 changes: 16 additions & 0 deletions docs/content/en/api/query-builder-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ Build custom endpoints.
</code-block>
</code-group>

## `config`
<alert type="success">Available in version >= v1.8.0</alert>

- Arguments: `(config)`
- Returns: `self`

Configuration of HTTP Instance.

```js
await Model.config({
method: 'PATCH',
header: { /* ... */ },
data: { foo: 'bar' }
}).save()
```

## `get`
- Returns: `Collection | { data: Collection }`

Expand Down
21 changes: 19 additions & 2 deletions docs/content/en/building-the-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,21 @@ We can build a resource to get the latest `Posts` that belongs to a **User**:
</code-block>
</code-group>

## Configuring the Request

<alert type="success">Available in version >= v1.8.0</alert>

See the [API reference](/api/query-builder-methods#config)

The `config` method can be used to configure the current request at query builder. We can pass any config available
from the HTTP Instance. If we are using [Axios](https://github.com/axios/axios),
we should pass an [AxiosRequestConfig](https://github.com/axios/axios#request-config).

We can add headers, change the method, anything we want:

```js
await Post.config({ headers: { /*...*/ } }).get()
```

## Needless Parent Request

Expand All @@ -586,8 +601,10 @@ We can get a list of **Posts** that belongs to an **User**:
</code-block>
</code-group>

And the same thing using for the example above, if we want to define a dynamic resource,
we can create a new **User** instance with the ID:
And the same thing can be done if we want to define a
[dynamic resource](/building-the-query#defining-a-dynamic-resource).

We can create a new **User** instance with the ID:

<code-group>
<code-block label="Query" active>
Expand Down
32 changes: 32 additions & 0 deletions docs/content/en/performing-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,38 @@ Then we can update our newly created **Post**:
</code-block>
</code-group>

And if we want to use `PATCH`, we can easily do that using [config](/api/query-builder-methods#config).

<code-group>
<code-block Label="Query" active>

```js
const post = await Post.find(1)

post.text = 'An updated text for our Post!'

await post.config({ method: 'PATCH' }).save()
```

</code-block>
<code-block Label="Find Request">

```http request
GET /posts/1
```

</code-block>
<code-block Label="Save Request">

```http request
PATCH /posts/1
```

</code-block>
</code-group>

<alert type="info">You can safely use `PATCH` with `save()`. The `POST` method will not be overridden, only `PUT`.</alert>

### Deleting a Model

See the [API reference](/api/crud-operations#delete).
Expand Down
102 changes: 70 additions & 32 deletions src/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export default class Model extends StaticModel {
return Model.$http
}

config(config) {
this._config = config
return this
}

resource() {
return `${this.constructor.name.toLowerCase()}s`
}
Expand Down Expand Up @@ -277,6 +282,25 @@ export default class Model extends StaticModel {
}
}

_reqConfig(config, options = { forceMethod: false }) {
const _config = { ...config, ...this._config }

if (options.forceMethod) {
_config.method = config.method
}

// Check if config has data
if ('data' in _config) {
// Ditch private data
_config.data = Object.fromEntries(
Object.entries(_config.data)
.filter(([key]) => !key.startsWith('_'))
)
}

return _config
}

first() {
return this.get().then(response => {
let item
Expand Down Expand Up @@ -304,10 +328,12 @@ export default class Model extends StaticModel {
let base = this._fromResource || `${this.baseURL()}/${this.resource()}`
let url = `${base}/${identifier}${this._builder.query()}`

return this.request({
url,
method: 'GET'
}).then(response => {
return this.request(
this._reqConfig({
url,
method: 'GET'
})
).then(response => {
return this._applyInstance(response.data)
})
}
Expand All @@ -327,10 +353,12 @@ export default class Model extends StaticModel {
base = this._customResource ? `${this.baseURL()}/${this._customResource}` : base
let url = `${base}${this._builder.query()}`

return this.request({
url,
method: 'GET'
}).then(response => {
return this.request(
this._reqConfig({
url,
method: 'GET'
})
).then(response => {
let collection = this._applyInstanceCollection(response.data)

if (response.data.data !== undefined) {
Expand Down Expand Up @@ -358,32 +386,38 @@ export default class Model extends StaticModel {
throw new Error('This model has a empty ID.')
}

return this.request({
url: this.endpoint(),
method: 'DELETE'
}).then(response => response)
return this.request(
this._reqConfig({
method: 'DELETE',
url: this.endpoint()
})
).then(response => response)
}

save() {
return this.hasId() ? this._update() : this._create()
}

_create() {
return this.request({
method: 'POST',
url: this.endpoint(),
data: this
}).then(response => {
return this.request(
this._reqConfig({
method: 'POST',
url: this.endpoint(),
data: this
}, { forceMethod: true })
).then(response => {
return this._applyInstance(response.data.data || response.data)
})
}

_update() {
return this.request({
method: 'PUT',
url: this.endpoint(),
data: this
}).then(response => {
return this.request(
this._reqConfig({
method: 'PUT',
url: this.endpoint(),
data: this
})
).then(response => {
return this._applyInstance(response.data.data || response.data)
})
}
Expand All @@ -393,18 +427,22 @@ export default class Model extends StaticModel {
*/

attach(params) {
return this.request({
method: 'POST',
url: this.endpoint(),
data: params
}).then(response => response)
return this.request(
this._reqConfig({
method: 'POST',
url: this.endpoint(),
data: params
})
).then(response => response)
}

sync(params) {
return this.request({
method: 'PUT',
url: this.endpoint(),
data: params
}).then(response => response)
return this.request(
this._reqConfig({
method: 'PUT',
url: this.endpoint(),
data: params
})
).then(response => response)
}
}
63 changes: 63 additions & 0 deletions tests/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,69 @@ describe('Model methods', () => {
comment.save()
})

test('save() method makes a PATCH request when method is set using `config`', async () => {
let post

axiosMock.onAny().reply((config) => {
const _post = post
delete _post._config

expect(config.method).toEqual('patch')
expect(config.data).toEqual(JSON.stringify(_post))
expect(config.url).toEqual('http://localhost/posts/1')

return [200, {}]
})

post = new Post({ id: 1, title: 'Cool!' })
await post.config({ method: 'PATCH' }).save()
})

test('save() method makes a POST request even when `config` set to PATCH', async () => {
let post
const _postResponse = {
id: 1,
title: 'Cool!',
text: 'Lorem Ipsum Dolor',
user: {
firstname: 'John',
lastname: 'Doe',
age: 25
},
relationships: {
tags: [
{
name: 'super'
},
{
name: 'awesome'
}
]
}
}

axiosMock.onAny().reply((config) => {
const _post = post
delete _post._config

expect(config.method).toEqual('post')
expect(config.data).toEqual(JSON.stringify(_post))
expect(config.url).toEqual('http://localhost/posts')

return [200, _postResponse]
})

post = new Post({ title: 'Cool!' })
post = await post.config({ method: 'PATCH' }).save()

expect(post).toEqual(_postResponse)
expect(post).toBeInstanceOf(Post)
expect(post.user).toBeInstanceOf(User)
post.relationships.tags.forEach(tag => {
expect(tag).toBeInstanceOf(Tag)
})
})

test('save() method makes a POST request when ID of object is null', async () => {
let post

Expand Down