Skip to content

Commit a771ed3

Browse files
authored
Merge pull request #1192 from Gijsreyn/gh-57/main/add-uri-function
Re-open add `uri()` function
2 parents ca5fefe + 3a37ffa commit a771ed3

File tree

8 files changed

+672
-1
lines changed

8 files changed

+672
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ utfx = { version = "0.1" }
194194
# dsc-lib
195195
uuid = { version = "1.18", features = ["v4"] }
196196
# dsc-lib
197+
url = { version = "2.5" }
198+
# dsc-lib
197199
urlencoding = { version = "2.1" }
198200
# dsc-lib
199201
which = { version = "8.0" }
Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
---
2+
description: Reference for the 'uri' DSC configuration document function
3+
ms.date: 01/10/2025
4+
ms.topic: reference
5+
title: uri
6+
---
7+
8+
# uri
9+
10+
## Synopsis
11+
12+
Creates an absolute URI by combining the baseUri and the relativeUri string.
13+
14+
## Syntax
15+
16+
```Syntax
17+
uri(<baseUri>, <relativeUri>)
18+
```
19+
20+
## Description
21+
22+
The `uri()` function combines a base URI with a relative URI to create an absolute URI according to
23+
[RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) URI resolution rules. This standardized behavior
24+
ensures consistent and predictable URI construction.
25+
26+
### Requirements
27+
28+
- **Base URI must be absolute**: The base URI must include a scheme (such as `https://`, `http://`,
29+
or `file://`). Relative URIs or URIs without schemes return an error.
30+
- **Base URI cannot be empty**: An empty base URI returns an error because an absolute URI requires
31+
a valid base.
32+
33+
### URI Resolution Behavior
34+
35+
The function follows RFC 3986 Section 5.2 (Relative Resolution) rules:
36+
37+
- **Absolute relative URIs**: If the relative URI contains a scheme (e.g.,
38+
`https://other.com/path`), it completely replaces the base URI.
39+
- **Protocol-relative URIs** (starting with `//`): The relative URI inherits the scheme from the
40+
base URI. For example, `uri('https://example.com/', '//cdn.example.org/assets')` returns
41+
`https://cdn.example.org/assets`.
42+
- **Path-absolute relative URIs** (starting with `/`): The relative path replaces the entire path
43+
of the base URI, keeping the scheme and authority. For example,
44+
`uri('https://example.com/old/path', '/new/path')` returns `https://example.com/new/path`.
45+
- **Path-relative URIs** (not starting with `/`): The relative path is merged with the base URI's
46+
path. The last segment of the base path is removed and replaced with the relative URI. For
47+
example, `uri('https://example.com/api/v1', 'users')` returns `https://example.com/api/users`.
48+
- **Empty relative URI**: Returns the base URI unchanged.
49+
50+
### Special Cases
51+
52+
- **Triple slash sequences** (`///`): Returns an error. Three or more consecutive slashes are
53+
invalid URI syntax.
54+
- **Path normalization**: The function automatically normalizes paths, resolving `.` (current
55+
directory) and `..` (parent directory) references. For example,
56+
`uri('https://example.com/', 'path/../other')` returns `https://example.com/other`.
57+
- **Query strings and fragments**: Query strings (`?query=value`) and fragments (`#section`) in the
58+
relative URI are preserved in the result.
59+
60+
Use this function to build API endpoints, file paths, or resource URLs dynamically from
61+
configuration parameters.
62+
63+
## Examples
64+
65+
### Example 1 - Build API endpoint with trailing slash
66+
67+
The following example combines a base API URL ending with a slash with a relative path.
68+
69+
```yaml
70+
# uri.example.1.dsc.config.yaml
71+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
72+
parameters:
73+
apiBase:
74+
type: string
75+
defaultValue: https://api.example.com/v1/
76+
resourcePath:
77+
type: string
78+
defaultValue: users/123
79+
resources:
80+
- name: Build API endpoint
81+
type: Microsoft.DSC.Debug/Echo
82+
properties:
83+
output:
84+
endpoint: "[uri(parameters('apiBase'), parameters('resourcePath'))]"
85+
```
86+
87+
```bash
88+
dsc config get --file uri.example.1.dsc.config.yaml
89+
```
90+
91+
```yaml
92+
results:
93+
- name: Build API endpoint
94+
type: Microsoft.DSC.Debug/Echo
95+
result:
96+
actualState:
97+
output:
98+
endpoint: https://api.example.com/v1/users/123
99+
messages: []
100+
hadErrors: false
101+
```
102+
103+
### Example 2 - Handle duplicate slashes
104+
105+
The following example shows how the function automatically handles cases where both the base URI
106+
ends with a slash and the relative URI begins with a slash.
107+
108+
```yaml
109+
# uri.example.2.dsc.config.yaml
110+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
111+
resources:
112+
- name: Combine URIs with duplicate slashes
113+
type: Microsoft.DSC.Debug/Echo
114+
properties:
115+
output:
116+
withDuplicateSlashes: "[uri('https://example.com/', '/api/data')]"
117+
result: The function combines the slashes into one
118+
```
119+
120+
```bash
121+
dsc config get --file uri.example.2.dsc.config.yaml
122+
```
123+
124+
```yaml
125+
results:
126+
- name: Combine URIs with duplicate slashes
127+
type: Microsoft.DSC.Debug/Echo
128+
result:
129+
actualState:
130+
output:
131+
withDuplicateSlashes: https://example.com/api/data
132+
result: The function combines the slashes into one
133+
messages: []
134+
hadErrors: false
135+
```
136+
137+
### Example 3 - Replace path segments
138+
139+
The following example demonstrates how `uri()` replaces the last path segment when the base URI
140+
doesn't end with a trailing slash. It uses the [`concat()`][01] function to build the base URL.
141+
142+
```yaml
143+
# uri.example.3.dsc.config.yaml
144+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
145+
parameters:
146+
currentVersion:
147+
type: string
148+
defaultValue: v1
149+
newVersion:
150+
type: string
151+
defaultValue: v2
152+
resources:
153+
- name: Update API version
154+
type: Microsoft.DSC.Debug/Echo
155+
properties:
156+
output:
157+
oldEndpoint: "[concat('https://api.example.com/', parameters('currentVersion'))]"
158+
newEndpoint: "[uri(concat('https://api.example.com/', parameters('currentVersion')), parameters('newVersion'))]"
159+
```
160+
161+
```bash
162+
dsc config get --file uri.example.3.dsc.config.yaml
163+
```
164+
165+
```yaml
166+
results:
167+
- name: Update API version
168+
type: Microsoft.DSC.Debug/Echo
169+
result:
170+
actualState:
171+
output:
172+
oldEndpoint: https://api.example.com/v1
173+
newEndpoint: https://api.example.com/v2
174+
messages: []
175+
hadErrors: false
176+
```
177+
178+
### Example 4 - Build resource URLs
179+
180+
The following example shows how to use `uri()` with [`concat()`][01] to build complete resource
181+
URLs from configuration parameters.
182+
183+
```yaml
184+
# uri.example.4.dsc.config.yaml
185+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
186+
parameters:
187+
storageAccount:
188+
type: string
189+
defaultValue: mystorageaccount
190+
containerName:
191+
type: string
192+
defaultValue: documents
193+
blobName:
194+
type: string
195+
defaultValue: report.pdf
196+
resources:
197+
- name: Build blob URL
198+
type: Microsoft.DSC.Debug/Echo
199+
properties:
200+
output:
201+
blobUrl: >-
202+
[uri(
203+
concat('https://', parameters('storageAccount'), '.blob.core.windows.net/'),
204+
concat(parameters('containerName'), '/', parameters('blobName'))
205+
)]
206+
```
207+
208+
```bash
209+
dsc config get --file uri.example.4.dsc.config.yaml
210+
```
211+
212+
```yaml
213+
results:
214+
- name: Build blob URL
215+
type: Microsoft.DSC.Debug/Echo
216+
result:
217+
actualState:
218+
output:
219+
blobUrl: https://mystorageaccount.blob.core.windows.net/documents/report.pdf
220+
messages: []
221+
hadErrors: false
222+
```
223+
224+
### Example 5 - Handle query strings and ports
225+
226+
The following example demonstrates that `uri()` preserves query strings and port numbers correctly.
227+
228+
```yaml
229+
# uri.example.5.dsc.config.yaml
230+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
231+
resources:
232+
- name: URI with special components
233+
type: Microsoft.DSC.Debug/Echo
234+
properties:
235+
output:
236+
withPort: "[uri('https://example.com:8080/', 'api')]"
237+
withQuery: "[uri('https://example.com/api/', 'search?q=test&limit=10')]"
238+
```
239+
240+
```bash
241+
dsc config get --file uri.example.5.dsc.config.yaml
242+
```
243+
244+
```yaml
245+
results:
246+
- name: URI with special components
247+
type: Microsoft.DSC.Debug/Echo
248+
result:
249+
actualState:
250+
output:
251+
withPort: https://example.com:8080/api
252+
withQuery: https://example.com/api/search?q=test&limit=10
253+
messages: []
254+
hadErrors: false
255+
```
256+
257+
### Example 6 - Protocol-relative URI
258+
259+
The following example shows how protocol-relative URIs (starting with `//`) inherit the scheme
260+
from the base URI.
261+
262+
```yaml
263+
# uri.example.6.dsc.config.yaml
264+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
265+
resources:
266+
- name: Protocol-relative URI
267+
type: Microsoft.DSC.Debug/Echo
268+
properties:
269+
output:
270+
result: "[uri('https://example.com/', '//cdn.example.org/assets')]"
271+
explanation: The relative URI inherits the https scheme from the base
272+
```
273+
274+
```bash
275+
dsc config get --file uri.example.6.dsc.config.yaml
276+
```
277+
278+
```yaml
279+
results:
280+
- name: Protocol-relative URI
281+
type: Microsoft.DSC.Debug/Echo
282+
result:
283+
actualState:
284+
output:
285+
result: https://cdn.example.org/assets
286+
explanation: The relative URI inherits the https scheme from the base
287+
messages: []
288+
hadErrors: false
289+
```
290+
291+
## Parameters
292+
293+
### baseUri
294+
295+
The base URI string. Must be an absolute URI containing a scheme (such as `https://`, `http://`, or
296+
`file://`). The function uses this as the foundation for resolving the relative URI according to
297+
RFC 3986 rules.
298+
299+
```yaml
300+
Type: string
301+
Required: true
302+
Position: 1
303+
```
304+
305+
### relativeUri
306+
307+
The relative URI string to combine with the base URI. Can be:
308+
309+
- An absolute URI (replaces the base entirely)
310+
- A protocol-relative URI starting with `//` (inherits scheme from base)
311+
- A path-absolute URI starting with `/` (replaces base path)
312+
- A path-relative URI (merges with base path)
313+
- An empty string (returns base unchanged)
314+
315+
This is combined with the base URI according to RFC 3986 URI resolution rules.
316+
317+
```yaml
318+
Type: string
319+
Required: true
320+
Position: 2
321+
```
322+
323+
## Output
324+
325+
The `uri()` function returns a string containing the absolute URI created by combining the base URI
326+
and relative URI according to RFC 3986 URI resolution rules.
327+
328+
```yaml
329+
Type: string
330+
```
331+
332+
## Related functions
333+
334+
- [`concat()`][01] - Concatenates multiple strings together
335+
- [`format()`][02] - Creates a formatted string from a template
336+
- [`substring()`][03] - Extracts a portion of a string
337+
- [`replace()`][04] - Replaces text in a string
338+
- [`split()`][05] - Splits a string into an array
339+
- [`parameters()`][06] - Retrieves parameter values
340+
341+
<!-- Link reference definitions -->
342+
[01]: ./concat.md
343+
[02]: ./format.md
344+
[03]: ./substring.md
345+
[04]: ./replace.md
346+
[05]: ./split.md
347+
[06]: ./parameters.md

0 commit comments

Comments
 (0)