@ventlio/tanstack-query 0.2.63 → 0.2.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/config/bootstrapQueryRequest.d.ts +2 -2
  2. package/dist/config/bootstrapQueryRequest.js +1 -1
  3. package/dist/config/useEnvironmentVariables.js +2 -2
  4. package/dist/config/useReactNativeEnv.d.ts +1 -0
  5. package/dist/config/useReactNativeEnv.js +5 -16
  6. package/dist/config/useReactNativeEnv.js.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +228 -30
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/model/Model.d.ts +14 -0
  12. package/dist/model/Model.js +93 -0
  13. package/dist/model/Model.js.map +1 -0
  14. package/dist/model/index.d.ts +2 -0
  15. package/dist/model/model.interface.d.ts +7 -0
  16. package/dist/model/useQueryModel.d.ts +2 -2
  17. package/dist/model/useQueryModel.js +84 -2
  18. package/dist/model/useQueryModel.js.map +1 -1
  19. package/dist/queries/useDeleteRequest.js +1 -1
  20. package/dist/queries/usePatchRequest.js +0 -1
  21. package/dist/queries/usePatchRequest.js.map +1 -1
  22. package/dist/queries/usePostRequest.d.ts +2 -1
  23. package/dist/queries/usePostRequest.js +7 -2
  24. package/dist/queries/usePostRequest.js.map +1 -1
  25. package/dist/request/make-request.d.ts +1 -1
  26. package/dist/request/make-request.js +41 -7
  27. package/dist/request/make-request.js.map +1 -1
  28. package/dist/request/request.interface.d.ts +5 -0
  29. package/dist/types/index.d.ts +8 -3
  30. package/package.json +8 -2
  31. package/src/config/bootstrapQueryRequest.ts +3 -3
  32. package/src/config/useEnvironmentVariables.ts +2 -2
  33. package/src/config/useReactNativeEnv.ts +5 -20
  34. package/src/model/Model.ts +107 -0
  35. package/src/model/index.ts +2 -0
  36. package/src/model/model.interface.ts +10 -0
  37. package/src/model/useQueryModel.ts +123 -3
  38. package/src/queries/useDeleteRequest.ts +1 -1
  39. package/src/queries/usePostRequest.ts +8 -2
  40. package/src/request/make-request.ts +42 -6
  41. package/src/request/request.interface.ts +5 -0
  42. package/src/types/index.ts +9 -3
@@ -41,7 +41,7 @@ const useDeleteRequest = (deleteOptions) => {
41
41
  internalDeleteOptions = internalDeleteOptions ?? {};
42
42
  internalDeleteOptions.enabled = true;
43
43
  await updatedPathAsync(link);
44
- await setOptionsAsync(deleteOptions);
44
+ await setOptionsAsync(internalDeleteOptions);
45
45
  return query.data;
46
46
  };
47
47
  return { destroy, ...query };
@@ -1,7 +1,6 @@
1
1
  import { useMutation } from '@tanstack/react-query';
2
2
  import { useEnvironmentVariables } from '../config/useEnvironmentVariables.js';
3
3
  import { useQueryHeaders } from '../config/useQueryHeaders.js';
4
- import 'react';
5
4
  import { scrollToTop } from '../helpers/scrollToTop.js';
6
5
  import 'axios';
7
6
  import { makeRequest } from '../request/make-request.js';
@@ -1 +1 @@
1
- {"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePatchRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,9 +1,10 @@
1
1
  import type { MutateOptions } from '@tanstack/react-query';
2
2
  import type { IRequestError, IRequestSuccess } from '../request';
3
3
  import type { DefaultRequestOptions } from './queries.interface';
4
- export declare const usePostRequest: <TResponse>({ path, isFormData, baseUrl, headers, }: {
4
+ export declare const usePostRequest: <TResponse>({ path, isFormData, baseUrl, headers, fileSelectors, }: {
5
5
  path: string;
6
6
  isFormData?: boolean | undefined;
7
+ fileSelectors?: string[] | undefined;
7
8
  } & DefaultRequestOptions) => {
8
9
  data: undefined;
9
10
  error: null;
@@ -1,16 +1,17 @@
1
1
  import { useQueryClient, useMutation } from '@tanstack/react-query';
2
2
  import { useEnvironmentVariables } from '../config/useEnvironmentVariables.js';
3
3
  import { useQueryHeaders } from '../config/useQueryHeaders.js';
4
- import 'react';
4
+ import { useReactNativeEnv } from '../config/useReactNativeEnv.js';
5
5
  import { scrollToTop } from '../helpers/scrollToTop.js';
6
6
  import 'axios';
7
7
  import { makeRequest } from '../request/make-request.js';
8
8
  import { HttpMethod } from '../request/request.enum.js';
9
9
 
10
- const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
10
+ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, fileSelectors, }) => {
11
11
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
12
12
  const queryClient = useQueryClient();
13
13
  const { getHeaders } = useQueryHeaders();
14
+ const { isApp } = useReactNativeEnv();
14
15
  const sendRequest = async (res, rej, postData) => {
15
16
  // get request headers
16
17
  const globalHeaders = getHeaders();
@@ -23,6 +24,10 @@ const usePostRequest = ({ path, isFormData = false, baseUrl, headers, }) => {
23
24
  headers: { ...globalHeaders, ...headers },
24
25
  baseURL: baseUrl ?? API_URL,
25
26
  timeout: TIMEOUT,
27
+ appFileConfig: {
28
+ isApp,
29
+ fileSelectors,
30
+ },
26
31
  });
27
32
  if (postResponse.status) {
28
33
  // scroll to top after success
@@ -1 +1 @@
1
- {"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePostRequest.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,2 +1,2 @@
1
1
  import type { IMakeRequest } from './request.interface';
2
- export declare function makeRequest<TResponse>({ body, method, path, isFormData, headers, baseURL, timeout, }: IMakeRequest): Promise<import("./request.interface").IRequestError>;
2
+ export declare function makeRequest<TResponse>({ body, method, path, isFormData, headers, baseURL, timeout, appFileConfig, }: IMakeRequest): Promise<import("./request.interface").IRequestError>;
@@ -1,22 +1,43 @@
1
+ import axios from 'axios';
1
2
  import { axiosInstance } from './axios-instance.js';
2
- import { buildFormData } from './buildFormData.js';
3
3
  import { ContentType, HttpMethod } from './request.enum.js';
4
4
  import { errorTransformer, successTransformer } from './transformer.js';
5
5
 
6
- async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, }) {
6
+ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, headers = {}, baseURL, timeout, appFileConfig, }) {
7
+ // check if file is included in mobile app environment and extract all file input to avoid
8
+ // it being formatted to object using axios formData builder
9
+ const isApp = appFileConfig?.isApp;
10
+ const appFiles = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
7
11
  // configure body
8
- body = isFormData ? buildFormData(body) : body;
9
- // configure request header
12
+ body = (isFormData ? axios.toFormData(body) : body);
13
+ // configure request header1
10
14
  if (!isFormData) {
11
15
  headers['Content-Type'] = ContentType.APPLICATION_JSON;
12
16
  }
13
17
  else {
14
- delete headers['Content-Type'];
18
+ if (isApp) {
19
+ headers['Content-Type'] = ContentType.MULTIPART_FORM_DATA;
20
+ // add the app files
21
+ for (const fileKey in appFiles) {
22
+ const currentFile = appFiles[fileKey];
23
+ if (Array.isArray(currentFile)) {
24
+ for (const innerFile of currentFile) {
25
+ body.append(fileKey, innerFile);
26
+ }
27
+ }
28
+ else {
29
+ body.append(fileKey, currentFile);
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ delete headers['Content-Type'];
35
+ }
15
36
  }
16
37
  try {
17
- const axios = axiosInstance({ baseURL, headers, timeout });
38
+ const axiosRequest = axiosInstance({ baseURL, headers, timeout });
18
39
  // send request
19
- const resp = await axios({
40
+ const resp = await axiosRequest({
20
41
  url: path,
21
42
  method,
22
43
  data: body,
@@ -44,6 +65,19 @@ async function makeRequest({ body, method = HttpMethod.GET, path, isFormData, he
44
65
  ...errorData,
45
66
  });
46
67
  }
68
+ }
69
+ function getAppFiles(body, fileSelectors = []) {
70
+ const files = {};
71
+ if (body) {
72
+ if (fileSelectors.length > 0) {
73
+ //
74
+ for (const fileKey of fileSelectors) {
75
+ files[fileKey] = body[fileKey];
76
+ delete body[fileKey];
77
+ }
78
+ }
79
+ }
80
+ return files;
47
81
  }
48
82
 
49
83
  export { makeRequest };
@@ -1 +1 @@
1
- {"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"make-request.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,6 +8,11 @@ export interface IMakeRequest {
8
8
  method?: HttpMethod;
9
9
  isFormData?: boolean;
10
10
  headers: RawAxiosRequestHeaders;
11
+ appFileConfig?: AppFileConfig;
12
+ }
13
+ export interface AppFileConfig {
14
+ fileSelectors?: string[];
15
+ isApp: boolean;
11
16
  }
12
17
  export interface AxiosInstanceOption {
13
18
  bearerToken?: string;
@@ -1,14 +1,19 @@
1
1
  import type { RawAxiosRequestHeaders } from 'axios';
2
- export interface BootstrapQueryRequest {
2
+ export interface BootstrapConfig {
3
3
  environments?: {
4
4
  appBaseUrl: string;
5
5
  appTimeout: number;
6
6
  };
7
- context?: 'app' | 'web' | 'electronjs';
7
+ context?: ContextType;
8
+ modelConfig?: BootstrapModelConfig;
8
9
  }
10
+ export interface BootstrapModelConfig {
11
+ idColumn: string;
12
+ }
13
+ export type ContextType = 'app' | 'web' | 'electronjs';
9
14
  export interface TanstackQueryConfig {
10
15
  headers: RawAxiosRequestHeaders;
11
- options?: BootstrapQueryRequest;
16
+ options?: BootstrapConfig;
12
17
  }
13
18
  export interface IUseQueryHeaders {
14
19
  getHeaders: () => TanstackQueryConfig['headers'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ventlio/tanstack-query",
3
- "version": "0.2.63",
3
+ "version": "0.2.64",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "contributors": [
@@ -51,6 +51,8 @@
51
51
  "@testing-library/react-hooks": "^8.0.1",
52
52
  "@types/axios": "^0.14.0",
53
53
  "@types/jest": "^29.5.1",
54
+ "@types/lodash.result": "^4.5.7",
55
+ "@types/lodash.set": "^4.3.7",
54
56
  "@types/node": "*",
55
57
  "@types/react": "^18.2.0",
56
58
  "@types/react-dom": "^18.2.0",
@@ -82,5 +84,9 @@
82
84
  "files": [
83
85
  "dist/**/*",
84
86
  "src"
85
- ]
87
+ ],
88
+ "dependencies": {
89
+ "lodash.result": "^4.5.2",
90
+ "lodash.set": "^4.3.2"
91
+ }
86
92
  }
@@ -1,14 +1,14 @@
1
1
  import type { QueryClient } from '@tanstack/react-query';
2
- import type { BootstrapQueryRequest, TanstackQueryConfig } from '../types';
2
+ import type { BootstrapConfig, TanstackQueryConfig } from '../types';
3
3
 
4
- export const bootstrapQueryRequest = (queryClient: QueryClient, options?: BootstrapQueryRequest): void => {
4
+ export const bootstrapQueryRequest = (queryClient: QueryClient, options?: BootstrapConfig): void => {
5
5
  // make query config doesn't expire
6
6
  queryClient.setQueryDefaults(['config'], {
7
7
  staleTime: Infinity,
8
8
  cacheTime: Infinity,
9
9
  });
10
10
 
11
- // set default query confg
11
+ // set default query config
12
12
  queryClient.setQueryData<TanstackQueryConfig>(['config'], {
13
13
  headers: {
14
14
  Authorization: ``,
@@ -3,8 +3,8 @@ import { useReactNativeEnv } from './useReactNativeEnv';
3
3
 
4
4
  export const useEnvironmentVariables = (): IConfig => {
5
5
  const { appTimeout, appUrl } = useReactNativeEnv();
6
- const url = process.env.REACT_APP_API_URL || process.env.NEXT_PUBLIC_API_URL || appUrl;
7
- const timeout = process.env.REACT_APP_API_TIMEOUT || process.env.NEXT_PUBLIC_API_TIMEOUT || appTimeout;
6
+ const url = process.env.REACT_APP_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? appUrl;
7
+ const timeout = process.env.REACT_APP_API_TIMEOUT ?? process.env.NEXT_PUBLIC_API_TIMEOUT ?? appTimeout;
8
8
 
9
9
  return {
10
10
  API_URL: url as string,
@@ -1,28 +1,13 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
- import { useEffect, useState } from 'react';
3
2
  import type { TanstackQueryConfig } from '../types';
4
3
 
5
4
  export const useReactNativeEnv = () => {
6
5
  const queryClient = useQueryClient();
6
+ const config = queryClient.getQueryData<TanstackQueryConfig>(['config']);
7
7
 
8
- const [appUrl, setAppUrl] = useState<string | undefined>(undefined);
9
- const [appTimeout, setAppTimeout] = useState<number | undefined>();
8
+ const appUrl: string | undefined = config?.options?.environments?.appBaseUrl;
9
+ const appTimeout: number | undefined = config?.options?.environments?.appTimeout;
10
+ const isApp = config?.options?.context === 'app';
10
11
 
11
- useEffect(() => {
12
- const config = queryClient.getQueryData<TanstackQueryConfig>(['config']);
13
-
14
- const loadReactNativeEnvIfNeeded = async () => {
15
- if (config?.options?.context === 'app') {
16
- const API_URL = config.options.environments?.appBaseUrl;
17
- const API_TIMEOUT = config.options.environments?.appTimeout;
18
-
19
- setAppUrl(API_URL);
20
- setAppTimeout(API_TIMEOUT);
21
- }
22
- };
23
-
24
- loadReactNativeEnvIfNeeded();
25
- }, [queryClient]);
26
-
27
- return { appUrl, appTimeout };
12
+ return { appUrl, appTimeout, isApp };
28
13
  };
@@ -0,0 +1,107 @@
1
+ import type { QueryClient } from '@tanstack/react-query';
2
+ import result from 'lodash.result';
3
+ import set from 'lodash.set';
4
+ import type { TanstackQueryConfig } from '../types';
5
+ import type { QueryModelBuilder } from './model.interface';
6
+
7
+ export class QueryModel<T> implements QueryModelBuilder<T> {
8
+ constructor(
9
+ private readonly queryKey: any[],
10
+ private readonly queryClient: QueryClient,
11
+ private readonly exact: boolean = true
12
+ ) {}
13
+
14
+ public findAll(path?: string): T[] | undefined {
15
+ const data = this.queryClient.getQueryData(this.queryKey, { exact: this.exact });
16
+
17
+ if (!data) {
18
+ return [];
19
+ }
20
+
21
+ if (!path) {
22
+ return Array.isArray(data) ? data : [data];
23
+ }
24
+
25
+ return result<T[]>(data, path, []);
26
+ }
27
+
28
+ public findMany(selector: (record: T) => boolean, path?: string): T[] {
29
+ const data = this.findAll(path) ?? [];
30
+ return data.filter(selector);
31
+ }
32
+
33
+ find(id: string | number, path?: string): T | undefined {
34
+ const modelConfig = this.getModelConfig();
35
+
36
+ if (!modelConfig?.idColumn) {
37
+ return undefined;
38
+ }
39
+ const data = this.findAll(path) ?? [];
40
+
41
+ return data.find((record) => (record as Record<string, any>)[modelConfig.idColumn] === id);
42
+ }
43
+
44
+ update(id: string | number, data: Partial<T>, path?: string): T | undefined {
45
+ const oldData = this.findAll(path) ?? [];
46
+ const modelConfig = this.getModelConfig();
47
+
48
+ if (!modelConfig?.idColumn) {
49
+ return undefined;
50
+ }
51
+ const idColumn = modelConfig.idColumn;
52
+
53
+ let updatedRecord: T | undefined = undefined;
54
+ const newData = oldData.map((record) => {
55
+ let dataRecord = record as Record<string, any>;
56
+
57
+ if (dataRecord[idColumn] === id) {
58
+ dataRecord = { ...dataRecord, ...data };
59
+ updatedRecord = dataRecord as T;
60
+ }
61
+
62
+ return dataRecord;
63
+ });
64
+
65
+ if (!path) {
66
+ this.queryClient.setQueryData(this.queryKey, newData);
67
+ } else {
68
+ const queryData = this.queryClient.getQueryData(this.queryKey, { exact: this.exact }) ?? {};
69
+ this.queryClient.setQueryData(this.queryKey, set(queryData, path, newData));
70
+ }
71
+ return updatedRecord;
72
+ }
73
+
74
+ remove(id: number | string, path?: string): boolean {
75
+ const oldData = this.findAll(path) ?? [];
76
+ const modelConfig = this.getModelConfig();
77
+
78
+ if (!modelConfig?.idColumn) {
79
+ return false;
80
+ }
81
+ const idColumn = modelConfig.idColumn;
82
+ let updated = false;
83
+ const newData = oldData.filter((record) => {
84
+ const dataRecord = record as Record<string, any>;
85
+ if (dataRecord[idColumn] === id) {
86
+ updated = true;
87
+ return false;
88
+ }
89
+ return true;
90
+ });
91
+
92
+ if (!path) {
93
+ this.queryClient.setQueryData(this.queryKey, newData);
94
+ } else {
95
+ const queryData = this.queryClient.getQueryData(this.queryKey, { exact: this.exact }) ?? {};
96
+ this.queryClient.setQueryData(this.queryKey, set(queryData, path, newData));
97
+ }
98
+ return updated;
99
+ }
100
+
101
+ private getModelConfig() {
102
+ const { options } = this.queryClient.getQueryData<TanstackQueryConfig>(['config']) ?? {};
103
+ const { modelConfig } = options ?? {};
104
+
105
+ return modelConfig;
106
+ }
107
+ }
@@ -1,3 +1,5 @@
1
+ export * from './Model';
2
+ export * from './model.interface';
1
3
  export * from './useKeyTrackerModel';
2
4
  export * from './useQueryModel';
3
5
  export * from './useRefetchQuery';
@@ -0,0 +1,10 @@
1
+ export interface QueryModelBuilder<T> {
2
+ add: (data: T, position?: QueryModelAddPosition, path?: string) => T | undefined;
3
+ findAll: (path?: string) => T[] | undefined;
4
+ findMany: (selector: (record: T) => boolean, path?: string) => T[];
5
+ find: (id: number | string, path?: string) => T | undefined;
6
+ update: (id: number | string, data: Partial<T>, path?: string) => T | undefined;
7
+ remove: (id: number, path?: string) => boolean;
8
+ }
9
+
10
+ export type QueryModelAddPosition = 'start' | 'end';
@@ -1,8 +1,128 @@
1
- import type { QueryFilters } from '@tanstack/react-query';
2
1
  import { useQueryClient } from '@tanstack/react-query';
2
+ import result from 'lodash.result';
3
+ import set from 'lodash.set';
4
+ import type { TanstackQueryConfig } from '../types';
5
+ import type { QueryModelAddPosition, QueryModelBuilder } from './model.interface';
6
+ import { useKeyTrackerModel } from './useKeyTrackerModel';
3
7
 
4
- export const useQueryModel = (queryKey: any[], filters?: QueryFilters | undefined) => {
8
+ export const useQueryModel = <T>(keyTracker: string, exact: boolean = true): QueryModelBuilder<T> => {
5
9
  const queryClient = useQueryClient();
10
+ const { getQueryKey } = useKeyTrackerModel(keyTracker);
11
+ const queryKey = getQueryKey() as any[];
6
12
 
7
- return queryClient.getQueryData(queryKey, filters);
13
+ const add = (data: T, position?: QueryModelAddPosition, path?: string): T | undefined => {
14
+ let records = findAll(path) ?? [];
15
+
16
+ if (!position || position === 'end') {
17
+ records = [...records, data];
18
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
19
+ } else if (position === 'start') {
20
+ records = [data, ...records];
21
+ }
22
+
23
+ if (!path) {
24
+ queryClient.setQueryData(queryKey, records);
25
+ } else {
26
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
27
+ queryClient.setQueryData(queryKey, set(queryData, path, records));
28
+ }
29
+
30
+ return data;
31
+ };
32
+
33
+ const findAll = (path?: string): T[] | undefined => {
34
+ const data = queryClient.getQueryData(queryKey, { exact });
35
+
36
+ if (!data) {
37
+ return [];
38
+ }
39
+
40
+ if (!path) {
41
+ return Array.isArray(data) ? data : [data];
42
+ }
43
+
44
+ return result<T[]>(data, path, []);
45
+ };
46
+
47
+ const findMany = (selector: (record: T) => boolean, path?: string): T[] => {
48
+ const data = findAll(path) ?? [];
49
+ return data.filter(selector);
50
+ };
51
+
52
+ const find = (id: string | number, path?: string): T | undefined => {
53
+ const modelConfig = getModelConfig();
54
+
55
+ if (!modelConfig?.idColumn) {
56
+ return undefined;
57
+ }
58
+ const data = findAll(path) ?? [];
59
+
60
+ return data.find((record) => (record as Record<string, any>)[modelConfig.idColumn] === id);
61
+ };
62
+
63
+ const getModelConfig = () => {
64
+ const { options } = queryClient.getQueryData<TanstackQueryConfig>(['config']) ?? {};
65
+ const { modelConfig } = options ?? {};
66
+
67
+ return modelConfig;
68
+ };
69
+
70
+ const update = (id: string | number, data: Partial<T>, path?: string): T | undefined => {
71
+ const oldData = findAll(path) ?? [];
72
+ const modelConfig = getModelConfig();
73
+
74
+ if (!modelConfig?.idColumn) {
75
+ return undefined;
76
+ }
77
+ const idColumn = modelConfig.idColumn;
78
+
79
+ let updatedRecord: T | undefined = undefined;
80
+ const newData = oldData.map((record) => {
81
+ let dataRecord = record as Record<string, any>;
82
+
83
+ if (dataRecord[idColumn] === id) {
84
+ dataRecord = { ...dataRecord, ...data };
85
+ updatedRecord = dataRecord as T;
86
+ }
87
+
88
+ return dataRecord;
89
+ });
90
+
91
+ if (!path) {
92
+ queryClient.setQueryData(queryKey, newData);
93
+ } else {
94
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
95
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
96
+ }
97
+ return updatedRecord;
98
+ };
99
+
100
+ const remove = (id: number | string, path?: string): boolean => {
101
+ const oldData = findAll(path) ?? [];
102
+ const modelConfig = getModelConfig();
103
+
104
+ if (!modelConfig?.idColumn) {
105
+ return false;
106
+ }
107
+ const idColumn = modelConfig.idColumn;
108
+ let updated = false;
109
+ const newData = oldData.filter((record) => {
110
+ const dataRecord = record as Record<string, any>;
111
+ if (dataRecord[idColumn] === id) {
112
+ updated = true;
113
+ return false;
114
+ }
115
+ return true;
116
+ });
117
+
118
+ if (!path) {
119
+ queryClient.setQueryData(queryKey, newData);
120
+ } else {
121
+ const queryData = queryClient.getQueryData(queryKey, { exact }) ?? {};
122
+ queryClient.setQueryData(queryKey, set(queryData, path, newData));
123
+ }
124
+ return updated;
125
+ };
126
+
127
+ return { find, findAll, findMany, remove, update, add };
8
128
  };
@@ -63,7 +63,7 @@ export const useDeleteRequest = <TResponse>(deleteOptions?: DefaultRequestOption
63
63
  internalDeleteOptions.enabled = true;
64
64
 
65
65
  await updatedPathAsync(link);
66
- await setOptionsAsync(deleteOptions);
66
+ await setOptionsAsync(internalDeleteOptions);
67
67
 
68
68
  return query.data;
69
69
  };
@@ -1,6 +1,6 @@
1
1
  import type { MutateOptions } from '@tanstack/react-query';
2
2
  import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { useEnvironmentVariables, useQueryHeaders } from '../config';
3
+ import { useEnvironmentVariables, useQueryHeaders, useReactNativeEnv } from '../config';
4
4
 
5
5
  import type { RawAxiosRequestHeaders } from 'axios';
6
6
  import { scrollToTop } from '../helpers';
@@ -14,14 +14,16 @@ export const usePostRequest = <TResponse>({
14
14
  isFormData = false,
15
15
  baseUrl,
16
16
  headers,
17
+ fileSelectors,
17
18
  }: {
18
19
  path: string;
19
20
  isFormData?: boolean;
21
+ fileSelectors?: string[];
20
22
  } & DefaultRequestOptions) => {
21
23
  const { API_URL, TIMEOUT } = useEnvironmentVariables();
22
24
  const queryClient = useQueryClient();
23
25
  const { getHeaders } = useQueryHeaders();
24
-
26
+ const { isApp } = useReactNativeEnv();
25
27
  const sendRequest = async (res: (value: any) => void, rej: (reason?: any) => void, postData: any) => {
26
28
  // get request headers
27
29
  const globalHeaders: RawAxiosRequestHeaders = getHeaders();
@@ -35,6 +37,10 @@ export const usePostRequest = <TResponse>({
35
37
  headers: { ...globalHeaders, ...headers },
36
38
  baseURL: baseUrl ?? API_URL,
37
39
  timeout: TIMEOUT,
40
+ appFileConfig: {
41
+ isApp,
42
+ fileSelectors,
43
+ },
38
44
  });
39
45
 
40
46
  if (postResponse.status) {
@@ -1,6 +1,6 @@
1
+ import axios from 'axios';
1
2
  import { axiosInstance } from './axios-instance';
2
3
 
3
- import { buildFormData } from './buildFormData';
4
4
  import { ContentType, HttpMethod } from './request.enum';
5
5
  import type { IMakeRequest } from './request.interface';
6
6
  import { errorTransformer, successTransformer } from './transformer';
@@ -13,22 +13,43 @@ export async function makeRequest<TResponse>({
13
13
  headers = {},
14
14
  baseURL,
15
15
  timeout,
16
+ appFileConfig,
16
17
  }: IMakeRequest) {
18
+ // check if file is included in mobile app environment and extract all file input to avoid
19
+ // it being formatted to object using axios formData builder
20
+ const isApp = appFileConfig?.isApp;
21
+ const appFiles: Record<string, string> = isApp ? getAppFiles(body, appFileConfig.fileSelectors) : {};
22
+
17
23
  // configure body
18
- body = isFormData ? buildFormData(body as Record<string, any>) : body;
24
+ body = (isFormData ? axios.toFormData(body as FormData) : body) as FormData;
19
25
 
20
- // configure request header
26
+ // configure request header1
21
27
  if (!isFormData) {
22
28
  headers['Content-Type'] = ContentType.APPLICATION_JSON;
23
29
  } else {
24
- delete headers['Content-Type'];
30
+ if (isApp) {
31
+ headers['Content-Type'] = ContentType.MULTIPART_FORM_DATA;
32
+ // add the app files
33
+ for (const fileKey in appFiles) {
34
+ const currentFile = appFiles[fileKey];
35
+ if (Array.isArray(currentFile)) {
36
+ for (const innerFile of currentFile) {
37
+ body.append(fileKey, innerFile);
38
+ }
39
+ } else {
40
+ body.append(fileKey, currentFile);
41
+ }
42
+ }
43
+ } else {
44
+ delete headers['Content-Type'];
45
+ }
25
46
  }
26
47
 
27
48
  try {
28
- const axios = axiosInstance({ baseURL, headers, timeout });
49
+ const axiosRequest = axiosInstance({ baseURL, headers, timeout });
29
50
 
30
51
  // send request
31
- const resp = await axios({
52
+ const resp = await axiosRequest({
32
53
  url: path,
33
54
  method,
34
55
  data: body,
@@ -60,3 +81,18 @@ export async function makeRequest<TResponse>({
60
81
  });
61
82
  }
62
83
  }
84
+ function getAppFiles(body: any, fileSelectors: string[] = []) {
85
+ const files: Record<string, string> = {};
86
+
87
+ if (body) {
88
+ if (fileSelectors.length > 0) {
89
+ //
90
+ for (const fileKey of fileSelectors) {
91
+ files[fileKey] = body[fileKey];
92
+ delete body[fileKey];
93
+ }
94
+ }
95
+ }
96
+
97
+ return files;
98
+ }