Skip to content

Commit 4852e49

Browse files
Update dotnet new umbraco-extension template with newer @hey-api/openapi-ts (#19825)
* Updated hey-api as the client-fetch is bundled as part of @hey-api/openapi-ts in newer versions * Regenerated a new package-lock.json file * Fix typescript issue * Update templates/UmbracoExtension/Client/package.json Co-authored-by: Jacob Overgaard <[email protected]> * Updated client dependencies * Vite, TypeScript, hey-api * Chalk & Cross-Env for the generate-client script * Explicitly remove package-lock.json as it will be out of sync due to UMBRACO_VERSION_FROM_TEMPLATE * Regenerated Hey API client that now ships client rather than dependancy * Vite and Hey-API were already out of date (updated to the very latest) --------- Co-authored-by: Jacob Overgaard <[email protected]>
1 parent 1c157b2 commit 4852e49

File tree

15 files changed

+1497
-42
lines changed

15 files changed

+1497
-42
lines changed

templates/UmbracoExtension/Client/package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
"generate-client": "node scripts/generate-openapi.js https://localhost:44339/umbraco/swagger/umbracoextension/swagger.json"
1010
},
1111
"devDependencies": {
12-
"@hey-api/client-fetch": "^0.10.0",
13-
"@hey-api/openapi-ts": "^0.66.7",
12+
"@hey-api/openapi-ts": "^0.80.14",
1413
"@umbraco-cms/backoffice": "^UMBRACO_VERSION_FROM_TEMPLATE",
15-
"chalk": "^5.4.1",
16-
"cross-env": "^7.0.3",
14+
"chalk": "^5.6.0",
15+
"cross-env": "^10.0.0",
1716
"node-fetch": "^3.3.2",
18-
"typescript": "^5.8.3",
19-
"vite": "^6.3.4"
17+
"typescript": "^5.9.2",
18+
"vite": "^7.1.3"
2019
}
2120
}

templates/UmbracoExtension/Client/scripts/generate-openapi.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,15 @@ fetch(swaggerUrl).then(async (response) => {
3232
console.log(`Calling ${chalk.yellow('hey-api')} to generate TypeScript client`);
3333

3434
await createClient({
35+
client: '@hey-api/client-fetch',
3536
input: swaggerUrl,
3637
output: 'src/api',
3738
plugins: [
3839
...defaultPlugins,
39-
'@hey-api/client-fetch',
40-
{
41-
name: '@hey-api/typescript',
42-
enums: 'typescript'
43-
},
4440
{
4541
name: '@hey-api/sdk',
46-
asClass: true
42+
asClass: true,
43+
classNameBuilder: '{{name}}Service',
4744
}
4845
],
4946
});

templates/UmbracoExtension/Client/src/api/client.gen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// This file is auto-generated by @hey-api/openapi-ts
22

33
import type { ClientOptions } from './types.gen';
4-
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch';
4+
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client';
55

66
/**
77
* The `createClientConfig()` function will be called on client initialization
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import type { Client, Config, ResolvedRequestOptions } from './types.gen';
4+
import {
5+
buildUrl,
6+
createConfig,
7+
createInterceptors,
8+
getParseAs,
9+
mergeConfigs,
10+
mergeHeaders,
11+
setAuthParams,
12+
} from './utils.gen';
13+
14+
type ReqInit = Omit<RequestInit, 'body' | 'headers'> & {
15+
body?: any;
16+
headers: ReturnType<typeof mergeHeaders>;
17+
};
18+
19+
export const createClient = (config: Config = {}): Client => {
20+
let _config = mergeConfigs(createConfig(), config);
21+
22+
const getConfig = (): Config => ({ ..._config });
23+
24+
const setConfig = (config: Config): Config => {
25+
_config = mergeConfigs(_config, config);
26+
return getConfig();
27+
};
28+
29+
const interceptors = createInterceptors<
30+
Request,
31+
Response,
32+
unknown,
33+
ResolvedRequestOptions
34+
>();
35+
36+
const request: Client['request'] = async (options) => {
37+
const opts = {
38+
..._config,
39+
...options,
40+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
41+
headers: mergeHeaders(_config.headers, options.headers),
42+
serializedBody: undefined,
43+
};
44+
45+
if (opts.security) {
46+
await setAuthParams({
47+
...opts,
48+
security: opts.security,
49+
});
50+
}
51+
52+
if (opts.requestValidator) {
53+
await opts.requestValidator(opts);
54+
}
55+
56+
if (opts.body && opts.bodySerializer) {
57+
opts.serializedBody = opts.bodySerializer(opts.body);
58+
}
59+
60+
// remove Content-Type header if body is empty to avoid sending invalid requests
61+
if (opts.serializedBody === undefined || opts.serializedBody === '') {
62+
opts.headers.delete('Content-Type');
63+
}
64+
65+
const url = buildUrl(opts);
66+
const requestInit: ReqInit = {
67+
redirect: 'follow',
68+
...opts,
69+
body: opts.serializedBody,
70+
};
71+
72+
let request = new Request(url, requestInit);
73+
74+
for (const fn of interceptors.request._fns) {
75+
if (fn) {
76+
request = await fn(request, opts);
77+
}
78+
}
79+
80+
// fetch must be assigned here, otherwise it would throw the error:
81+
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
82+
const _fetch = opts.fetch!;
83+
let response = await _fetch(request);
84+
85+
for (const fn of interceptors.response._fns) {
86+
if (fn) {
87+
response = await fn(response, request, opts);
88+
}
89+
}
90+
91+
const result = {
92+
request,
93+
response,
94+
};
95+
96+
if (response.ok) {
97+
if (
98+
response.status === 204 ||
99+
response.headers.get('Content-Length') === '0'
100+
) {
101+
return opts.responseStyle === 'data'
102+
? {}
103+
: {
104+
data: {},
105+
...result,
106+
};
107+
}
108+
109+
const parseAs =
110+
(opts.parseAs === 'auto'
111+
? getParseAs(response.headers.get('Content-Type'))
112+
: opts.parseAs) ?? 'json';
113+
114+
let data: any;
115+
switch (parseAs) {
116+
case 'arrayBuffer':
117+
case 'blob':
118+
case 'formData':
119+
case 'json':
120+
case 'text':
121+
data = await response[parseAs]();
122+
break;
123+
case 'stream':
124+
return opts.responseStyle === 'data'
125+
? response.body
126+
: {
127+
data: response.body,
128+
...result,
129+
};
130+
}
131+
132+
if (parseAs === 'json') {
133+
if (opts.responseValidator) {
134+
await opts.responseValidator(data);
135+
}
136+
137+
if (opts.responseTransformer) {
138+
data = await opts.responseTransformer(data);
139+
}
140+
}
141+
142+
return opts.responseStyle === 'data'
143+
? data
144+
: {
145+
data,
146+
...result,
147+
};
148+
}
149+
150+
const textError = await response.text();
151+
let jsonError: unknown;
152+
153+
try {
154+
jsonError = JSON.parse(textError);
155+
} catch {
156+
// noop
157+
}
158+
159+
const error = jsonError ?? textError;
160+
let finalError = error;
161+
162+
for (const fn of interceptors.error._fns) {
163+
if (fn) {
164+
finalError = (await fn(error, response, request, opts)) as string;
165+
}
166+
}
167+
168+
finalError = finalError || ({} as string);
169+
170+
if (opts.throwOnError) {
171+
throw finalError;
172+
}
173+
174+
// TODO: we probably want to return error and improve types
175+
return opts.responseStyle === 'data'
176+
? undefined
177+
: {
178+
error: finalError,
179+
...result,
180+
};
181+
};
182+
183+
return {
184+
buildUrl,
185+
connect: (options) => request({ ...options, method: 'CONNECT' }),
186+
delete: (options) => request({ ...options, method: 'DELETE' }),
187+
get: (options) => request({ ...options, method: 'GET' }),
188+
getConfig,
189+
head: (options) => request({ ...options, method: 'HEAD' }),
190+
interceptors,
191+
options: (options) => request({ ...options, method: 'OPTIONS' }),
192+
patch: (options) => request({ ...options, method: 'PATCH' }),
193+
post: (options) => request({ ...options, method: 'POST' }),
194+
put: (options) => request({ ...options, method: 'PUT' }),
195+
request,
196+
setConfig,
197+
trace: (options) => request({ ...options, method: 'TRACE' }),
198+
};
199+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
export type { Auth } from '../core/auth.gen';
4+
export type { QuerySerializerOptions } from '../core/bodySerializer.gen';
5+
export {
6+
formDataBodySerializer,
7+
jsonBodySerializer,
8+
urlSearchParamsBodySerializer,
9+
} from '../core/bodySerializer.gen';
10+
export { buildClientParams } from '../core/params.gen';
11+
export { createClient } from './client.gen';
12+
export type {
13+
Client,
14+
ClientOptions,
15+
Config,
16+
CreateClientConfig,
17+
Options,
18+
OptionsLegacyParser,
19+
RequestOptions,
20+
RequestResult,
21+
ResolvedRequestOptions,
22+
ResponseStyle,
23+
TDataShape,
24+
} from './types.gen';
25+
export { createConfig, mergeHeaders } from './utils.gen';

0 commit comments

Comments
 (0)