Skip to content

Commit aa47b9b

Browse files
authored
Merge pull request #15 from Chantouch/feature/accept-query-string-as-object
feat(query): 🚀 accept object as query string
2 parents 62a82c7 + f4ca956 commit aa47b9b

File tree

9 files changed

+82037
-27926
lines changed

9 files changed

+82037
-27926
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"sourceType": "module"
1515
// "project": "./tsconfig.json"
1616
},
17-
"plugins": ["@typescript-eslint", "prettier"],
17+
"plugins": ["@typescript-eslint", "prettier", "promise", "import"],
1818
"rules": {
1919
"prettier/prettier": ["error"],
2020
"@typescript-eslint/no-explicit-any": 0,

package-lock.json

Lines changed: 68660 additions & 15123 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-api-queries",
3-
"version": "0.0.15",
3+
"version": "0.0.16",
44
"description": "Elegant and simple way to build requests for REST API",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -58,12 +58,13 @@
5858
"@types/jest": "^26.0.14",
5959
"@types/lodash.merge": "^4.6.6",
6060
"@types/node": "^14.11.10",
61-
"@typescript-eslint/eslint-plugin": "^4.4.1",
61+
"@typescript-eslint/eslint-plugin": "^4.15.1",
6262
"@typescript-eslint/parser": "^4.4.1",
6363
"axios-mock-adapter": "^1.18.2",
6464
"bootstrap-vue": "^2.17.3",
65-
"eslint": "^7.11.0",
65+
"eslint": "^7.2.0",
6666
"eslint-config-prettier": "^6.13.0",
67+
"eslint-plugin-import": "^2.22.1",
6768
"eslint-plugin-prettier": "^3.1.4",
6869
"husky": "^4.3.0",
6970
"jest": "^26.5.3",
@@ -85,6 +86,7 @@
8586
"camelcase-keys": "^6.2.2",
8687
"escape-string-regexp": "^4.0.0",
8788
"lodash.merge": "^4.6.2",
89+
"qs": "^6.9.6",
8890
"snakecase-keys": "^3.2.0"
8991
}
9092
}

src/__tests__/base-proxy.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('BaseProxy', () => {
1919
BaseProxy.$http = axios
2020
proxy = new PostProxy()
2121
mockAdapter = new MockAdapter(axios)
22+
mockAdapter.reset()
2223
})
2324

2425
it('check if http was installed', async () => {
@@ -72,6 +73,15 @@ describe('BaseProxy', () => {
7273
expect(data).toEqual(items)
7374
})
7475

76+
it('Check network server return 500', async () => {
77+
mockAdapter.onGet('/posts').networkError()
78+
try {
79+
await proxy.all()
80+
} catch (e) {
81+
expect(e.message).toBe('Network Error')
82+
}
83+
})
84+
7585
it('will fetch all records with query params', async () => {
7686
const items = [
7787
{ first_name: 'Dara', last_name: 'Hok', id: 1 },
@@ -120,6 +130,26 @@ describe('BaseProxy', () => {
120130
.all()
121131
expect(data).toEqual(items)
122132
})
133+
it('it should accept query params as object', async () => {
134+
const items = [
135+
{ first_name: 'Dara', last_name: 'Hok', id: 1 },
136+
{ first_name: 'Chantouch', last_name: 'Sek', id: 2 },
137+
]
138+
mockAdapter
139+
.onGet('/posts?search[id]=1&first_name=Dara')
140+
.reply(200, { data: items })
141+
const params = {
142+
search: { id: 1 },
143+
first_name: 'Dara',
144+
last_name: 'Hok',
145+
}
146+
const { data } = await proxy
147+
.setParameters(params)
148+
.removeParameters(['last_name'])
149+
.all()
150+
expect(data).toEqual(items)
151+
expect(proxy.parameters).toEqual({ search: { id: 1 }, first_name: 'Dara' })
152+
})
123153

124154
it('it should find an item by id', async () => {
125155
const item = { first_name: 'Chantouch', last_name: 'Sek', id: 1 }
@@ -145,13 +175,14 @@ describe('BaseProxy', () => {
145175

146176
it('transforms the data to a FormData object if there is a File', async () => {
147177
const file = new File(['hello world!'], 'myfile')
148-
const form = { field1: {}, field2: {} }
178+
const form = { field1: {}, field2: {}, files: [] }
149179
form.field1 = {
150180
foo: 'testFoo',
151181
bar: ['testBar1', 'testBar2'],
152182
baz: new Date(Date.UTC(2012, 3, 13, 2, 12)),
153183
}
154184
form.field2 = file
185+
form.files = [{ file }]
155186

156187
mockAdapter.onPost('/posts').reply((request) => {
157188
expect(request.data).toBeInstanceOf(FormData)
@@ -160,13 +191,15 @@ describe('BaseProxy', () => {
160191
expect(request.data.get('field1[bar][1]')).toBe('testBar2')
161192
expect(request.data.get('field1[baz]')).toBe('2012-04-13T02:12:00.000Z')
162193
expect(request.data.get('field2')).toEqual(file)
194+
expect(request.data.get('files[0][file]')).toEqual(file)
163195

164196
expect(getFormDataKeys(request.data)).toEqual([
165197
'field1[foo]',
166198
'field1[bar][0]',
167199
'field1[bar][1]',
168200
'field1[baz]',
169201
'field2',
202+
'files[0][file]',
170203
])
171204
return [200, {}]
172205
})

src/__tests__/validator.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ describe('Validator', () => {
4444
expect(validator.missed('name')).toBeTruthy()
4545
expect(validator.nullState('name')).toBeNull()
4646
})
47+
test('Check nullState function', () => {
48+
validator.add('email', 'The email field is required.')
49+
expect(validator.nullState('email')).toBeFalsy()
50+
})
51+
test('Get an none exist error messages by param name', () => {
52+
expect(validator.get('email')).toHaveLength(0)
53+
})
4754
test('Get all errors by key', () => {
4855
validator.add('email', 'The email field is required.')
4956
expect(validator.get('email').length).toBeGreaterThan(0)
@@ -68,6 +75,13 @@ describe('Validator', () => {
6875
expect(validator.first('email')).toBe('The email field is required.')
6976
expect(Object.keys(validator.all()).length).toEqual(2)
7077
})
78+
it('should not allow protopath overwrite', () => {
79+
const errors = {
80+
__proto__: ['Set __proto__ to overwrite.'],
81+
}
82+
validator.fill(errors)
83+
expect(Object.keys(validator.all()).length).toEqual(0)
84+
})
7185
test('Clear all errors by flush', () => {
7286
const errors = {
7387
name: ['The name field is required.'],

src/core/BaseProxy.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { AxiosError, AxiosInstance, AxiosResponse, Method } from 'axios'
2-
import { isFile, isArray } from '../util/objects'
3-
import type { Errors } from '../'
2+
import { isArray, isFile } from '../util/objects'
3+
import type { Errors } from '..'
44
import Validator from './Validator'
55
import { objectToFormData } from '../util/formData'
6+
import qs from 'qs'
67

78
const validator = Validator
89
const UNPROCESSABLE_ENTITY = 422
@@ -12,9 +13,13 @@ export interface ParametersType {
1213

1314
class BaseProxy {
1415
public errors: Errors
16+
1517
public parameters: any | any[]
18+
1619
public readonly endpoint: string
20+
1721
public static $http: AxiosInstance
22+
1823
public static $errorsKeyName = 'errors'
1924

2025
constructor(endpoint: string, parameters?: any | any[]) {
@@ -52,7 +57,7 @@ class BaseProxy {
5257
}
5358

5459
putWithFile(id: string | number, payload: any): Promise<any> {
55-
payload['_method'] = 'put'
60+
payload._method = 'put'
5661
return this.submit('post', `/${this.endpoint}/${id}`, payload)
5762
}
5863

@@ -89,18 +94,15 @@ class BaseProxy {
8994
}
9095
reject(error)
9196
} else {
92-
reject()
97+
reject(error)
9398
}
9499
})
95100
})
96101
}
97102

98103
__getParameterString(url: string): string {
99-
const keys = Object.keys(this.parameters)
100-
const parameters = keys
101-
.filter((key: string) => !!this.parameters[key])
102-
.map((key: string) => `${key}=${this.parameters[key]}`)
103-
return parameters.length === 0 ? url : `${url}?${parameters.join('&')}`
104+
const query = qs.stringify(this.parameters, { encode: false })
105+
return query ? `${url}?${query}` : url
104106
}
105107

106108
__getQueryString(parameter: string): any | any[] {

src/util/formData.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ export function objectToFormData(
77
return formData.append(parent, object)
88
}
99
for (const property in object) {
10-
if (object.hasOwnProperty(property)) {
11-
appendToFormData(formData, getKey(parent, property), object[property])
10+
if (!object.hasOwnProperty(property)) {
11+
continue
1212
}
13+
appendToFormData(formData, getKey(parent, property), object[property])
1314
}
1415
return formData
1516
}

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"types": [
1212
"@types/node",
1313
"@nuxt/types",
14-
"@types/jest"
14+
"@types/jest",
15+
"axios"
1516
]
1617
},
1718
"include": ["src"],

0 commit comments

Comments
 (0)