Skip to content

Commit dcd89bf

Browse files
authored
feat(typescript-angular): add util "provideApi" and update docs to standalone applications (#21173)
* feat(angular): add util "provideApi" for standalone applications (Angular 17 and above) * feat(angular): update README with new provideApi usage examples for Angular applications * feat(angular): update README to reflect new provideApi import path and usage examples * feat(angular): add support for README_beforeV17 and update provideApi return type * feat(angular): add support for README_beforeV17 and update provideApi return type * feat(angular): add support for README_beforeV17 and update provideApi return type * fix: correct casing in DuplicateTest and FooDuplicateTest headers * feat(angular): add provideApi function and update README with usage examples
1 parent d2196dd commit dcd89bf

File tree

33 files changed

+979
-1001
lines changed

33 files changed

+979
-1001
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,30 +176,42 @@ public String getHelp() {
176176
@Override
177177
public void processOpts() {
178178
super.processOpts();
179+
180+
// determine NG version
181+
SemVer ngVersion;
182+
if (additionalProperties.containsKey(NG_VERSION)) {
183+
ngVersion = new SemVer(additionalProperties.get(NG_VERSION).toString());
184+
} else {
185+
ngVersion = new SemVer(this.ngVersion);
186+
LOGGER.info("generating code for Angular {} ...", ngVersion);
187+
LOGGER.info(" (you can select the angular version by setting the additionalProperties (--additional-properties in CLI) ngVersion)");
188+
}
189+
boolean ngVersionAtLeast_17 = ngVersion.atLeast("17.0.0");
190+
179191
supportingFiles.add(
180192
new SupportingFile("models.mustache", modelPackage().replace('.', File.separatorChar), "models.ts"));
181193
supportingFiles
182194
.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts"));
183195
supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts"));
184196
supportingFiles.add(new SupportingFile("api.module.mustache", getIndexDirectory(), "api.module.ts"));
197+
if (ngVersionAtLeast_17) {
198+
supportingFiles.add(new SupportingFile("provide-api.mustache", getIndexDirectory(), "provide-api.ts"));
199+
}
185200
supportingFiles.add(new SupportingFile("configuration.mustache", getIndexDirectory(), "configuration.ts"));
186201
supportingFiles.add(new SupportingFile("api.base.service.mustache", getIndexDirectory(), "api.base.service.ts"));
187202
supportingFiles.add(new SupportingFile("variables.mustache", getIndexDirectory(), "variables.ts"));
188203
supportingFiles.add(new SupportingFile("encoder.mustache", getIndexDirectory(), "encoder.ts"));
189204
supportingFiles.add(new SupportingFile("param.mustache", getIndexDirectory(), "param.ts"));
190205
supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
191206
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
192-
supportingFiles.add(new SupportingFile("README.mustache", getIndexDirectory(), "README.md"));
193207

194-
// determine NG version
195-
SemVer ngVersion;
196-
if (additionalProperties.containsKey(NG_VERSION)) {
197-
ngVersion = new SemVer(additionalProperties.get(NG_VERSION).toString());
198-
} else {
199-
ngVersion = new SemVer(this.ngVersion);
200-
LOGGER.info("generating code for Angular {} ...", ngVersion);
201-
LOGGER.info(" (you can select the angular version by setting the additionalProperties (--additional-properties in CLI) ngVersion)");
208+
if(ngVersionAtLeast_17) {
209+
supportingFiles.add(new SupportingFile("README.mustache", getIndexDirectory(), "README.md"));
202210
}
211+
else {
212+
supportingFiles.add(new SupportingFile("README_beforeV17.mustache", getIndexDirectory(), "README.md"));
213+
}
214+
203215

204216
if (!ngVersion.atLeast("9.0.0")) {
205217
throw new IllegalArgumentException("Invalid ngVersion: " + ngVersion + ". Only Angular v9+ is supported.");
@@ -250,6 +262,7 @@ public void processOpts() {
250262
}
251263

252264
additionalProperties.put(NG_VERSION, ngVersion);
265+
additionalProperties.put("ngVersionAtLeast_17", ngVersionAtLeast_17);
253266

254267
if (additionalProperties.containsKey(API_MODULE_PREFIX)) {
255268
String apiModulePrefix = additionalProperties.get(API_MODULE_PREFIX).toString();

modules/openapi-generator/src/main/resources/typescript-angular/README.mustache

Lines changed: 73 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -58,157 +58,106 @@ Published packages are not effected by this issue.
5858
In your Angular project:
5959

6060
```typescript
61-
// without configuring providers
62-
import { {{apiModuleClassName}} } from '{{npmName}}';
63-
import { HttpClientModule } from '@angular/common/http';
64-
65-
@NgModule({
66-
imports: [
67-
{{apiModuleClassName}},
68-
// make sure to import the HttpClientModule in the AppModule only,
69-
// see https://github.com/angular/angular/issues/20575
70-
HttpClientModule
71-
],
72-
declarations: [ AppComponent ],
73-
providers: [],
74-
bootstrap: [ AppComponent ]
75-
})
76-
export class AppModule {}
77-
```
7861

79-
```typescript
80-
// configuring providers
81-
import { {{apiModuleClassName}}, {{configurationClassName}}, {{configurationParametersInterfaceName}} } from '{{npmName}}';
82-
83-
export function apiConfigFactory (): {{configurationClassName}} {
84-
const params: {{configurationParametersInterfaceName}} = {
85-
// set configuration parameters here.
86-
}
87-
return new {{configurationClassName}}(params);
88-
}
89-
90-
@NgModule({
91-
imports: [ {{apiModuleClassName}}.forRoot(apiConfigFactory) ],
92-
declarations: [ AppComponent ],
93-
providers: [],
94-
bootstrap: [ AppComponent ]
95-
})
96-
export class AppModule {}
97-
```
62+
import { ApplicationConfig } from '@angular/core';
63+
import { provideHttpClient } from '@angular/common/http';
64+
import { provideApi } from '{{npmName}}';
9865

99-
```typescript
100-
// configuring providers with an authentication service that manages your access tokens
101-
import { {{apiModuleClassName}}, {{configurationClassName}} } from '{{npmName}}';
102-
103-
@NgModule({
104-
imports: [ {{apiModuleClassName}} ],
105-
declarations: [ AppComponent ],
66+
export const appConfig: ApplicationConfig = {
10667
providers: [
107-
{
108-
provide: {{configurationClassName}},
109-
useFactory: (authService: AuthService) => new {{configurationClassName}}(
110-
{
111-
basePath: environment.apiUrl,
112-
accessToken: authService.getAccessToken.bind(authService)
113-
}
114-
),
115-
deps: [AuthService],
116-
multi: false
117-
}
68+
// ...
69+
provideHttpClient(),
70+
provideApi()
11871
],
119-
bootstrap: [ AppComponent ]
120-
})
121-
export class AppModule {}
72+
};
12273
```
12374

75+
**NOTE**
76+
If you're still using `AppModule` and haven't [migrated](https://angular.dev/reference/migrations/standalone) yet, you can still import an Angular module:
12477
```typescript
125-
import { DefaultApi } from '{{npmName}}';
126-
127-
export class AppComponent {
128-
constructor(private apiGateway: DefaultApi) { }
129-
}
78+
import { {{apiModuleClassName}} } from '{{npmName}}';
13079
```
13180

132-
Note: The {{apiModuleClassName}} is restricted to being instantiated once app wide.
133-
This is to ensure that all services are treated as singletons.
134-
135-
### Using multiple OpenAPI files / APIs / {{apiModuleClassName}}s
136-
137-
In order to use multiple `{{apiModuleClassName}}s` generated from different OpenAPI files,
138-
you can create an alias name when importing the modules
139-
in order to avoid naming conflicts:
81+
If different from the generated base path, during app bootstrap, you can provide the base path to your service.
14082

14183
```typescript
142-
import { {{apiModuleClassName}} } from 'my-api-path';
143-
import { {{apiModuleClassName}} as OtherApiModule } from 'my-other-api-path';
144-
import { HttpClientModule } from '@angular/common/http';
145-
146-
@NgModule({
147-
imports: [
148-
{{apiModuleClassName}},
149-
OtherApiModule,
150-
// make sure to import the HttpClientModule in the AppModule only,
151-
// see https://github.com/angular/angular/issues/20575
152-
HttpClientModule
153-
]
154-
})
155-
export class AppModule {
84+
import { ApplicationConfig } from '@angular/core';
85+
import { provideHttpClient } from '@angular/common/http';
86+
import { provideApi } from '{{npmName}}';
15687

157-
}
88+
export const appConfig: ApplicationConfig = {
89+
providers: [
90+
// ...
91+
provideHttpClient(),
92+
provideApi('http://localhost:9999')
93+
],
94+
};
15895
```
15996

160-
### Set service base path
161-
162-
If different than the generated base path, during app bootstrap, you can provide the base path to your service.
163-
16497
```typescript
165-
import { BASE_PATH } from '{{npmName}}';
98+
// with a custom configuration
99+
import { ApplicationConfig } from '@angular/core';
100+
import { provideHttpClient } from '@angular/common/http';
101+
import { provideApi } from '{{npmName}}';
166102

167-
bootstrap(AppComponent, [
168-
{ provide: BASE_PATH, useValue: 'https://your-web-service.com' },
169-
]);
103+
export const appConfig: ApplicationConfig = {
104+
providers: [
105+
// ...
106+
provideHttpClient(),
107+
provideApi({
108+
withCredentials: true,
109+
username: 'user',
110+
password: 'password'
111+
})
112+
],
113+
};
170114
```
171115

172-
or
173-
174116
```typescript
175-
import { BASE_PATH } from '{{npmName}}';
176-
177-
@NgModule({
178-
imports: [],
179-
declarations: [ AppComponent ],
180-
providers: [ provide: BASE_PATH, useValue: 'https://your-web-service.com' ],
181-
bootstrap: [ AppComponent ]
182-
})
183-
export class AppModule {}
184-
```
185-
186-
### Using @angular/cli
187-
188-
First extend your `src/environments/*.ts` files by adding the corresponding base path:
117+
// with factory building a custom configuration
118+
import { ApplicationConfig } from '@angular/core';
119+
import { provideHttpClient } from '@angular/common/http';
120+
import { provideApi, {{configurationClassName}} } from '{{npmName}}';
189121

190-
```typescript
191-
export const environment = {
192-
production: false,
193-
API_BASE_PATH: 'http://127.0.0.1:8080'
122+
export const appConfig: ApplicationConfig = {
123+
providers: [
124+
// ...
125+
provideHttpClient(),
126+
{
127+
provide: {{configurationClassName}},
128+
useFactory: (authService: AuthService) => new {{configurationClassName}}({
129+
basePath: 'http://localhost:9999',
130+
withCredentials: true,
131+
username: authService.getUsername(),
132+
password: authService.getPassword(),
133+
}),
134+
deps: [AuthService],
135+
multi: false
136+
}
137+
],
194138
};
195139
```
196140

197-
In the src/app/app.module.ts:
141+
### Using multiple OpenAPI files / APIs
142+
143+
In order to use multiple APIs generated from different OpenAPI files,
144+
you can create an alias name when importing the modules
145+
in order to avoid naming conflicts:
198146

199147
```typescript
200-
import { BASE_PATH } from '{{npmName}}';
148+
import { provideApi as provideUserApi } from 'my-user-api-path';
149+
import { provideApi as provideAdminApi } from 'my-admin-api-path';
150+
import { HttpClientModule } from '@angular/common/http';
201151
import { environment } from '../environments/environment';
202152

203-
@NgModule({
204-
declarations: [
205-
AppComponent
206-
],
207-
imports: [ ],
208-
providers: [{ provide: BASE_PATH, useValue: environment.API_BASE_PATH }],
209-
bootstrap: [ AppComponent ]
210-
})
211-
export class AppModule { }
153+
export const appConfig: ApplicationConfig = {
154+
providers: [
155+
// ...
156+
provideHttpClient(),
157+
provideUserApi(environment.basePath),
158+
provideAdminApi(environment.basePath),
159+
],
160+
};
212161
```
213162

214163
### Customizing path parameter encoding

0 commit comments

Comments
 (0)