Skip to content

Commit bc2be19

Browse files
committed
Add support for dynamic variables in API requests
Introduced the ability to use configurable variables in API URLs, headers, and bodies for the remote-select plugin. Updated relevant service methods, configuration, and documentation to support this feature. Minor type and field adjustments were also applied for better consistency. Resolved: #4
1 parent 12b46b1 commit bc2be19

6 files changed

Lines changed: 102 additions & 11 deletions

File tree

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,41 @@ Advanced settings:
7474
| Multi mode | `string` | |
7575
| Private field | `string` | |
7676

77+
78+
## Variables in API Requests
79+
The plugin now supports using variables in your API URLs, headers, and request bodies.
80+
This allows for dynamic configuration of your API requests.
81+
82+
### Setting up variables
83+
Add variables to your plugin configuration in `config/plugins.js`:
84+
85+
``` js
86+
module.exports = {
87+
"remote-select": {
88+
enabled: true,
89+
variables: {
90+
apiBaseUrl: "https://api.example.com",
91+
apiVersion: "v2",
92+
authToken: "your-auth-token"
93+
}
94+
},
95+
};
96+
```
97+
98+
### Using variables
99+
You can use variables in your configuration by surrounding the variable name with curly braces:
100+
- In API URLs: `{apiBaseUrl}/products/{apiVersion}/list`
101+
- In request headers: `Authorization: Bearer {authToken}`
102+
- In request bodies: `{ "version": "{apiVersion}" }`
103+
104+
Variables provide a convenient way to:
105+
- Manage environment-specific API endpoints
106+
- Share authentication tokens across multiple select configurations
107+
- Update common values in one place instead of modifying each select configuration
108+
109+
If a variable isn't defined in your configuration, the placeholder will remain unchanged in the request.
110+
111+
77112
### Remote select input
78113

79114
Depends on `multi` option you will have in the model a single string from selected `value` option or array of selected `value` string.
@@ -169,3 +204,4 @@ and as a result, we have: (single mode)
169204
multiple mode:
170205

171206
![Searchable remote select multi input](https://github.com/dmitriy-nz/strapi-plugin-remote-select/raw/main/screenshots/searchable-remote-select-input.multiple.gif)
207+

admin/src/components/RemoteSelect/registerRemoteSelect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function registerRemoteSelect(app: any): void {
88
app.customFields.register({
99
name: "remote-select",
1010
pluginId: pluginId,
11-
type: "string",
11+
type: "text",
1212
intlLabel: {
1313
id: getTrad("remote-select.label"),
1414
defaultMessage: "Remote select",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "strapi-plugin-remote-select",
3-
"version": "1.0.7",
3+
"version": "1.0.8",
44
"description": "A powerful tool that adds select type inputs to your strapi with the ability to dynamically load options via API. Supports static and searchable endpoints—autocomplete.",
55
"keywords": [
66
"strapi select",

server/config/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import { RemoteSelectPluginOptions } from "../../types/RemoteSelectPluginOptions";
2+
13
export default {
2-
default: {},
4+
default: {
5+
variables: {},
6+
} as RemoteSelectPluginOptions,
37
validator() {},
48
};

server/services/OptionsProxy.service.ts

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,43 @@ import { Strapi } from "@strapi/strapi";
22
import * as jp from "jsonpath";
33
import { FlexibleSelectMappingConfig } from "../../types/FlexibleSelectConfig";
44
import { RemoteSelectFetchOptions } from "../../types/RemoteSelectFetchOptions";
5+
import { RemoteSelectPluginOptions } from "../../types/RemoteSelectPluginOptions";
56
import { SearchableRemoteSelectValue } from "../../types/SearchableRemoteSelectValue";
67

78
export default ({ strapi }: { strapi: Strapi }) => ({
9+
/**
10+
* Fetches options based on a provided configuration object, processes the response,
11+
* and maps the data into the desired format.
12+
*
13+
* @param config - The configuration object containing fetch details,
14+
* including URL, method, headers, body, and mapping instructions for processing the response.
15+
* @return A promise that resolves to the processed options extracted and mapped from the response.
16+
*/
817
async getOptionsByConfig(config: RemoteSelectFetchOptions) {
9-
const res = await fetch(config.fetch.url, {
18+
const res = await fetch(this.replaceVariables(config.fetch.url), {
1019
method: config.fetch.method,
1120
headers: this.parseStringHeaders(config.fetch.headers),
12-
body: config.fetch.body,
21+
body: config.fetch.body ? this.replaceVariables(config.fetch.body) : "",
1322
});
23+
1424
const response = await res.json();
1525

1626
return this.parseOptions(response, config.mapping);
1727
},
1828

29+
/**
30+
* Parses a string of headers into an object where each key is a header name and each value is the corresponding header value.
31+
*
32+
* @param [headers] - A string representing the headers, where each header is separated by a newline and the key-value pairs are separated by a colon.
33+
* @return An object containing the parsed headers where the keys are the header names in lowercase, and the values are the corresponding header values.
34+
*/
1935
parseStringHeaders(headers?: string): Record<string, string> {
2036
if (!headers) return {};
2137

2238
const result: Record<string, string> = {};
2339

40+
headers = this.replaceVariables(headers);
41+
2442
const headersArr = this.trim(headers).split("\n");
2543

2644
for (let i = 0; i < headersArr.length; i++) {
@@ -39,10 +57,23 @@ export default ({ strapi }: { strapi: Strapi }) => ({
3957
return result;
4058
},
4159

60+
/**
61+
* Removes leading and trailing whitespace characters from a given string.
62+
*
63+
* @param {string} val - The string to be trimmed.
64+
* @return {string} The trimmed string without leading or trailing whitespace.
65+
*/
4266
trim(val: string): string {
4367
return val.replace(/^\s+|\s+$/g, "");
4468
},
4569

70+
/**
71+
* Parses options from the provided response using the given mapping configuration.
72+
*
73+
* @param {any} response - The JSON response to parse and extract options from.
74+
* @param mappingConfig - The configuration defining the paths for extracting values and labels.
75+
* @return {SearchableRemoteSelectValue[]} An array of unique options with `value` and `label` properties.
76+
*/
4677
parseOptions(
4778
response: any,
4879
mappingConfig: FlexibleSelectMappingConfig,
@@ -80,11 +111,6 @@ export default ({ strapi }: { strapi: Strapi }) => ({
80111
};
81112
});
82113

83-
/**
84-
* Map variable that stores unique values of SearchableRemoteSelectValue objects.
85-
*
86-
* @type {Map<string, SearchableRemoteSelectValue>}
87-
*/
88114
const uniqueValuesOptionsMap: Map<string, SearchableRemoteSelectValue> =
89115
preparedOptionsArray.reduce(
90116
(
@@ -100,7 +126,7 @@ export default ({ strapi }: { strapi: Strapi }) => ({
100126
);
101127

102128
/**
103-
*
129+
* Convert Map to array of unique values
104130
*/
105131
return Array.from(uniqueValuesOptionsMap.values());
106132
},
@@ -127,4 +153,26 @@ export default ({ strapi }: { strapi: Strapi }) => ({
127153

128154
return value;
129155
},
156+
157+
/**
158+
* Replaces variables in a given string with corresponding values from the configuration.
159+
* Variables in the input string are denoted by `{variableName}`.
160+
*
161+
* @param {string} str - The input string containing variables to be replaced.
162+
* @return {string} The string with variables replaced by their corresponding values.
163+
* If a variable does not exist in the configuration, it remains unchanged.
164+
*/
165+
replaceVariables(str: string): string {
166+
const variables =
167+
strapi.config.get<RemoteSelectPluginOptions>("plugin.remote-select")
168+
?.variables ?? {};
169+
170+
if (!str || typeof str !== "string") {
171+
return str;
172+
}
173+
174+
return str.replace(/\{([^}]+)\}/g, (match, key) => {
175+
return variables[key] !== undefined ? String(variables[key]) : match;
176+
});
177+
},
130178
});

types/RemoteSelectPluginOptions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface RemoteSelectPluginOptions {
2+
variables: Record<string, string | number>;
3+
}

0 commit comments

Comments
 (0)