Skip to content

Commit 43f3bc5

Browse files
authored
[0.3.x] Allow config to override initial values (#24)
* wip * wip * wip
1 parent 78cb86c commit 43f3bc5

File tree

8 files changed

+209
-65
lines changed

8 files changed

+209
-65
lines changed

packages/alpine/src/index.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { Alpine as TAlpine } from 'alpinejs'
2-
import { client, Config, createValidator, RequestMethod, resolveName, toSimpleValidationErrors, ValidationConfig } from 'laravel-precognition'
2+
import { client, Config, createValidator, RequestMethod, resolveName, toSimpleValidationErrors, ValidationConfig, resolveUrl, resolveMethod } from 'laravel-precognition'
33
import cloneDeep from 'lodash.clonedeep'
44
import get from 'lodash.get'
55
import set from 'lodash.set'
66
import { Form } from './types'
77

88
export default function (Alpine: TAlpine) {
9-
Alpine.magic('form', (el) => <Data extends Record<string, unknown>>(method: RequestMethod, url: string, inputs: Data, config: ValidationConfig = {}): Data&Form<Data> => {
10-
// @ts-expect-error
11-
method = method.toLowerCase()
12-
13-
syncWithDom(el, method, url)
9+
Alpine.magic('form', (el) => <Data extends Record<string, unknown>>(method: RequestMethod|(() => RequestMethod), url: string|(() => string), inputs: Data, config: ValidationConfig = {}): Data&Form<Data> => {
10+
syncWithDom(el, resolveMethod(method), resolveUrl(url))
1411

1512
/**
1613
* The original data.
@@ -36,7 +33,7 @@ export default function (Alpine: TAlpine) {
3633
/**
3734
* The validator instance.
3835
*/
39-
const validator = createValidator(client => client[method](url, form.data(), config), originalData)
36+
const validator = createValidator(client => client[resolveMethod(method)](resolveUrl(url), form.data(), config), originalData)
4037
.on('validatingChanged', () => {
4138
form.validating = validator.validating()
4239
})
@@ -140,7 +137,7 @@ export default function (Alpine: TAlpine) {
140137
},
141138
processing: false,
142139
async submit(config = {}) {
143-
return client[method](url, form.data(), resolveSubmitConfig(config))
140+
return client[resolveMethod(method)](resolveUrl(url), form.data(), resolveSubmitConfig(config))
144141
},
145142
validateFiles() {
146143
validator.validateFiles()

packages/core/src/client.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isAxiosError, isCancel, AxiosInstance, AxiosResponse, default as Axios } from 'axios'
22
import merge from 'lodash.merge'
3-
import { Config, Client, RequestFingerprintResolver, StatusHandler, SuccessResolver } from './types'
3+
import { Config, Client, RequestFingerprintResolver, StatusHandler, SuccessResolver, RequestMethod } from './types'
44

55
/**
66
* The configured axios client.
@@ -26,11 +26,11 @@ const abortControllers: Record<string, AbortController> = {}
2626
* The precognitive HTTP client instance.
2727
*/
2828
export const client: Client = {
29-
get: (url, data = {}, config = {}) => request({ ...config, params: merge(config.params, data), url, method: 'get' }),
30-
post: (url, data = {}, config = {}) => request({ ...config, url, data, method: 'post' }),
31-
patch: (url, data = {}, config = {}) => request({ ...config, url, data, method: 'patch' }),
32-
put: (url, data = {}, config = {}) => request({ ...config, url, data, method: 'put' }),
33-
delete: (url, data = {}, config = {}) => request({ ...config, url, params: merge(config.params, data), method: 'delete' }),
29+
get: (url, data = {}, config = {}) => request(mergeConfig('get', url, data, config)),
30+
post: (url, data = {}, config = {}) => request(mergeConfig('post', url, data, config)),
31+
patch: (url, data = {}, config = {}) => request(mergeConfig('patch', url, data, config)),
32+
put: (url, data = {}, config = {}) => request(mergeConfig('put', url, data, config)),
33+
delete: (url, data = {}, config = {}) => request(mergeConfig('delete', url, data, config)),
3434
use(client) {
3535
axiosClient = client
3636

@@ -50,6 +50,20 @@ export const client: Client = {
5050
},
5151
}
5252

53+
/**
54+
* Merge the client specified arguments with the provided configuration.
55+
*/
56+
const mergeConfig = (method: RequestMethod, url: string, data?: Record<string, unknown>, config?: Config) => ({
57+
url,
58+
method,
59+
...config,
60+
...(['get', 'delete'].includes(method) ? {
61+
params: merge({}, data, config?.params),
62+
} : {
63+
data: merge({}, data, config?.data),
64+
}),
65+
})
66+
5367
/**
5468
* Send and handle a new request.
5569
*/
@@ -215,3 +229,17 @@ const hasFiles = (data: unknown): boolean => isFile(data)
215229
export const isFile = (value: unknown): boolean => (typeof File !== 'undefined' && value instanceof File)
216230
|| value instanceof Blob
217231
|| (typeof FileList !== 'undefined' && value instanceof FileList && value.length > 0)
232+
233+
/**
234+
* Resolve the url from a potential callback.
235+
*/
236+
export const resolveUrl = (url: string|(() => string)): string => typeof url === 'string'
237+
? url
238+
: url()
239+
240+
/**
241+
* Resolve the method from a potential callback.
242+
*/
243+
export const resolveMethod = (method: RequestMethod|(() => RequestMethod)): RequestMethod => typeof method === 'string'
244+
? method.toLowerCase() as RequestMethod
245+
: method()

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { client } from './client'
1+
export { client, resolveUrl, resolveMethod } from './client'
22
export { createValidator, toSimpleValidationErrors, resolveName } from './validator'
33
export * from './types'

packages/core/tests/client.test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,132 @@ it('does not revaldata when data is unchanged', async () => {
513513
expect(requests).toBe(2)
514514
vi.advanceTimersByTime(1500)
515515
})
516+
517+
it('overrides request method url with config url', async () => {
518+
expect.assertions(5)
519+
520+
let config
521+
axios.request.mockImplementation((c) => {
522+
config = c
523+
return Promise.resolve({ headers: { precognition: 'true' }, status: 204, data: 'data' })
524+
})
525+
526+
await client.get('https://laravel.com', {}, {
527+
url: 'https://forge.laravel.com',
528+
})
529+
expect(config.url).toBe('https://forge.laravel.com')
530+
531+
await client.post('https://laravel.com', {}, {
532+
url: 'https://forge.laravel.com',
533+
})
534+
expect(config.url).toBe('https://forge.laravel.com')
535+
536+
await client.patch('https://laravel.com', {}, {
537+
url: 'https://forge.laravel.com',
538+
})
539+
expect(config.url).toBe('https://forge.laravel.com')
540+
541+
await client.put('https://laravel.com', {}, {
542+
url: 'https://forge.laravel.com',
543+
})
544+
expect(config.url).toBe('https://forge.laravel.com')
545+
546+
await client.delete('https://laravel.com', {}, {
547+
url: 'https://forge.laravel.com',
548+
})
549+
expect(config.url).toBe('https://forge.laravel.com')
550+
})
551+
552+
it('overrides the request data with the config data', async () => {
553+
expect.assertions(5)
554+
555+
let config
556+
axios.request.mockImplementation((c) => {
557+
config = c
558+
return Promise.resolve({ headers: { precognition: 'true' }, status: 204, data: 'data' })
559+
})
560+
561+
await client.get('https://laravel.com', { expected: false }, {
562+
data: { expected: true }
563+
})
564+
expect(config.data).toEqual({ expected: true})
565+
566+
await client.post('https://laravel.com', { expected: false }, {
567+
data: { expected: true }
568+
})
569+
expect(config.data).toEqual({ expected: true})
570+
571+
await client.patch('https://laravel.com', { expected: false }, {
572+
data: { expected: true }
573+
})
574+
expect(config.data).toEqual({ expected: true})
575+
576+
await client.put('https://laravel.com', { expected: false }, {
577+
data: { expected: true }
578+
})
579+
expect(config.data).toEqual({ expected: true})
580+
581+
await client.delete('https://laravel.com', { expected: false }, {
582+
data: { expected: true }
583+
})
584+
expect(config.data).toEqual({ expected: true})
585+
})
586+
587+
it('merges request data with config data', async () => {
588+
expect.assertions(7)
589+
590+
let config
591+
axios.request.mockImplementation((c) => {
592+
config = c
593+
return Promise.resolve({ headers: { precognition: 'true' }, status: 204, data: 'data' })
594+
})
595+
596+
await client.get('https://laravel.com', { request: true }, {
597+
data: { config: true }
598+
})
599+
expect(config.data).toEqual({ config: true })
600+
expect(config.params).toEqual({ request: true })
601+
602+
await client.post('https://laravel.com', { request: true }, {
603+
data: { config: true }
604+
})
605+
expect(config.data).toEqual({ request: true, config: true })
606+
607+
await client.patch('https://laravel.com', { request: true }, {
608+
data: { config: true }
609+
})
610+
expect(config.data).toEqual({ request: true, config: true })
611+
612+
await client.put('https://laravel.com', { request: true }, {
613+
data: { config: true }
614+
})
615+
expect(config.data).toEqual({ request: true, config: true })
616+
617+
await client.delete('https://laravel.com', { request: true }, {
618+
data: { config: true }
619+
})
620+
expect(config.data).toEqual({ config: true })
621+
expect(config.params).toEqual({ request: true })
622+
})
623+
624+
it('merges request data with config params for get and delete requests', async () => {
625+
expect.assertions(4)
626+
627+
let config
628+
axios.request.mockImplementation((c) => {
629+
config = c
630+
return Promise.resolve({ headers: { precognition: 'true' }, status: 204, data: 'data' })
631+
})
632+
633+
await client.get('https://laravel.com', { data: true }, {
634+
params: { param: true }
635+
})
636+
expect(config.params).toEqual({ data: true, param: true })
637+
expect(config.data).toBeUndefined()
638+
639+
await client.delete('https://laravel.com', { data: true }, {
640+
params: { param: true }
641+
})
642+
expect(config.params).toEqual({ data: true, param: true })
643+
expect(config.data).toBeUndefined()
644+
})

packages/react-inertia/src/index.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import { Config, NamedInputEvent, RequestMethod, SimpleValidationErrors, toSimpleValidationErrors, ValidationConfig, ValidationErrors } from 'laravel-precognition'
1+
import { Config, NamedInputEvent, RequestMethod, SimpleValidationErrors, toSimpleValidationErrors, ValidationConfig, ValidationErrors, resolveUrl, resolveMethod } from 'laravel-precognition'
22
import { useForm as usePrecognitiveForm } from 'laravel-precognition-react'
33
import { useForm as useInertiaForm } from '@inertiajs/react'
44
import { useRef } from 'react'
55

6-
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod, url: string, inputs: Data, config: ValidationConfig = {}): any => {
7-
// @ts-expect-error
8-
method = method.toLowerCase()
9-
6+
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod|(() => RequestMethod), url: string|(() => string), inputs: Data, config: ValidationConfig = {}): any => {
107
const booted = useRef<boolean>(false)
118

129
/**
@@ -132,27 +129,28 @@ export const useForm = <Data extends Record<string, unknown>>(method: RequestMet
132129
submit(submitMethod: RequestMethod|Config = {}, submitUrl?: string, submitOptions?: any): void {
133130
const isPatchedCall = typeof submitMethod !== 'string'
134131

135-
const userOptions = isPatchedCall
132+
submitOptions = isPatchedCall
136133
? submitMethod
137134
: submitOptions
138135

139-
const options = {
140-
...userOptions,
136+
submitUrl = isPatchedCall
137+
? resolveUrl(url)
138+
: submitUrl!
139+
140+
submitMethod = isPatchedCall
141+
? resolveMethod(method)
142+
: submitMethod as RequestMethod
143+
144+
inertiaSubmit(submitMethod, submitUrl, {
145+
...submitOptions,
141146
onError: (errors: SimpleValidationErrors): any => {
142147
precognitiveForm.validator().setErrors(errors)
143148

144-
if (userOptions.onError) {
145-
return userOptions.onError(errors)
149+
if (submitOptions.onError) {
150+
return submitOptions.onError(errors)
146151
}
147152
},
148-
}
149-
150-
inertiaSubmit(
151-
isPatchedCall ? method : submitMethod,
152-
// @ts-expect-error
153-
(isPatchedCall ? url : submitUrl),
154-
options
155-
)
153+
})
156154
},
157155
validator: precognitiveForm.validator,
158156
})

packages/react/src/index.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import { resolveName, client, createValidator, Config, RequestMethod, Validator, toSimpleValidationErrors, ValidationConfig } from 'laravel-precognition'
1+
import { resolveName, client, createValidator, Config, RequestMethod, Validator, toSimpleValidationErrors, ValidationConfig, resolveUrl, resolveMethod } from 'laravel-precognition'
22
import cloneDeep from 'lodash.clonedeep'
33
import get from 'lodash.get'
44
import set from 'lodash.set'
55
import { useRef, useState } from 'react'
66
import { Form } from './types'
77

8-
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod, url: string, input: Data, config: ValidationConfig = {}): Form<Data> => {
9-
// @ts-expect-error
10-
method = method.toLowerCase()
11-
8+
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod|(() => RequestMethod), url: string|(() => string), input: Data, config: ValidationConfig = {}): Form<Data> => {
129
/**
1310
* The original data.
1411
*/
@@ -73,7 +70,7 @@ export const useForm = <Data extends Record<string, unknown>>(method: RequestMet
7370
const validator = useRef<Validator|null>(null)
7471

7572
if (validator.current === null) {
76-
validator.current = createValidator(client => client[method](url, payload.current, config), input)
73+
validator.current = createValidator(client => client[resolveMethod(method)](resolveUrl(url), payload.current, config), input)
7774
.on('validatingChanged', () => {
7875
setValidating(validator.current!.validating())
7976
})
@@ -193,7 +190,7 @@ export const useForm = <Data extends Record<string, unknown>>(method: RequestMet
193190
},
194191
processing,
195192
async submit(config = {}) {
196-
return client[method](url, payload.current, resolveSubmitConfig(config))
193+
return client[resolveMethod(method)](resolveUrl(url), payload.current, resolveSubmitConfig(config))
197194
},
198195
validateFiles() {
199196
validator.current!.validateFiles()

packages/vue-inertia/src/index.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { Config, NamedInputEvent, RequestMethod, SimpleValidationErrors, toSimpleValidationErrors, ValidationConfig, ValidationErrors } from 'laravel-precognition'
1+
import { Config, NamedInputEvent, RequestMethod, SimpleValidationErrors, toSimpleValidationErrors, ValidationConfig, ValidationErrors, resolveUrl, resolveMethod } from 'laravel-precognition'
22
import { useForm as usePrecognitiveForm } from 'laravel-precognition-vue'
33
import { useForm as useInertiaForm } from '@inertiajs/vue3'
44

5-
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod, url: string, inputs: Data, config: ValidationConfig = {}): any => {
6-
// @ts-expect-error
7-
method = method.toLowerCase()
8-
5+
export const useForm = <Data extends Record<string, unknown>>(method: RequestMethod|(() => RequestMethod), url: string|(() => string), inputs: Data, config: ValidationConfig = {}): any => {
96
/**
107
* The Inertia form.
118
*/
@@ -113,27 +110,28 @@ export const useForm = <Data extends Record<string, unknown>>(method: RequestMet
113110
submit(submitMethod: RequestMethod|Config = {}, submitUrl?: string, submitOptions?: any): void {
114111
const isPatchedCall = typeof submitMethod !== 'string'
115112

116-
const userOptions = isPatchedCall
113+
submitOptions = isPatchedCall
117114
? submitMethod
118115
: submitOptions
119116

120-
const options = {
121-
...userOptions,
117+
submitUrl = isPatchedCall
118+
? resolveUrl(url)
119+
: submitUrl!
120+
121+
submitMethod = isPatchedCall
122+
? resolveMethod(method)
123+
: submitMethod as RequestMethod
124+
125+
inertiaSubmit(submitMethod, submitUrl, {
126+
...submitOptions,
122127
onError: (errors: SimpleValidationErrors): any => {
123128
precognitiveForm.validator().setErrors(errors)
124129

125-
if (userOptions.onError) {
126-
return userOptions.onError(errors)
130+
if (submitOptions.onError) {
131+
return submitOptions.onError(errors)
127132
}
128133
},
129-
}
130-
131-
inertiaSubmit(
132-
isPatchedCall ? method : submitMethod,
133-
// @ts-expect-error
134-
(isPatchedCall ? url : submitUrl),
135-
options
136-
)
134+
})
137135
},
138136
validator: precognitiveForm.validator,
139137
})

0 commit comments

Comments
 (0)