Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/en/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -809,14 +809,15 @@ abp generate-proxy -t csharp -url https://localhost:44302/
- `csharp`: C#, work in the `*.HttpApi.Client` project directory. There are some additional options for this client:
- `--without-contracts`: Avoid generating the application service interface, class, enum and dto types.
- `--folder`: Folder name to place generated CSharp code in. Default value: `ClientProxies`.
- `ng`: Angular. There are some additional options for this client:
- `--api-name` or `-a`: The name of the API endpoint defined in the `/src/environments/environment.ts`. Default value: `default`.
- `--source` or `-s`: Specifies the Angular project name to resolve the root namespace & API definition URL from. Default value: `defaultProject`.
- `--target`: Specifies the Angular project name to place generated code in. Default value: `defaultProject`.
- `--module`: Backend module name. Default value: `app`.
- `--entry-point`: Targets the Angular project to place the generated code.
- `--url`: Specifies api definition url. Default value is API Name's url in environment file.
- `--prompt` or `-p`: Asks the options from the command line prompt (for the unspecified options).
- `ng`: Angular. There are some additional options for this client:
- `--api-name` or `-a`: The name of the API endpoint defined in the `/src/environments/environment.ts`. Default value: `default`.
- `--source` or `-s`: Specifies the Angular project name to resolve the root namespace & API definition URL from. Default value: `defaultProject`.
- `--target`: Specifies the Angular project name to place generated code in. Default value: `defaultProject`.
- `--module`: Backend module name. Default value: `app`.
- `--entry-point`: Targets the Angular project to place the generated code.
- `--url`: Specifies api definition url. Default value is API Name's url in environment file.
- `--resource-api`: Adds optional Resource API helpers for `GET` endpoints while keeping the generated Observable services. This parameter requires Angular v22 or later.
- `--prompt` or `-p`: Asks the options from the command line prompt (for the unspecified options).
- `js`: JavaScript. work in the `*.Web` project directory. There are some additional options for this client:
- `--output` or `-o`: JavaScript file path or folder to place generated code in.
- `--module` or `-m`: Specifies the name of the backend module you wish to generate proxies for. Default value: `app`.
Expand Down Expand Up @@ -1314,4 +1315,3 @@ var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(

- [Examples for the new command](./new-command-samples.md)
- [Video tutorial](https://abp.io/video-courses/essentials/abp-cli)

5 changes: 4 additions & 1 deletion docs/en/framework/ui/angular/service-proxies.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ export const environment: Config.Environment = {
- **target:** Target for the Angular project to place the generated code. For example, if it's `permission-management`, it'll look like this (npm/ng-packs/packages/*permission-management*).
- **entryPoint:** To create the generated proxy folder in the target. The directory is `permission-management/proxy/src/lib/proxy` and the `permission-management` is the value of target. If you want to create a folder for the generated proxy, there are two options, you should either set the value `proxy` as the entryPoint or go to project.json and change the `sourceRoot` from `packages/permission-management/src` to `packages/permission-management/proxy/src`. No need to change the sourceRoot of project with the property. if you keep it empty, the proxy will be generated into the folder defined in the sourceRoot property.
- **serviceType:** The service type of the generated proxy. The options are `application`, `integration` and `all`. The default value is `application`. A developer can mark a service "integration service". If you want to skip proxy generation for the service, then this is the correct setting. More info about [Integration Services](../../api-development/integration-services.md)
- **resourceApi:** Generates optional `rxResource` helpers for `GET` endpoints. This is off by default and keeps the current Observable-based services unchanged. This parameter requires Angular v22 or later


### Services

The `generate-proxy` command generates one service per back-end controller and a method (property with a function value actually) for each action in the controller. These methods call backend APIs via [RestService](./http-requests#restservice).

If you pass `--resource-api`, the generator keeps those methods and adds matching `rxResource` helpers for read operations.

A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's `RemoteServiceName`. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./http-requests#how-to-get-a-specific-api-endpoint-from-application-config)

The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting as shown below:
Expand Down Expand Up @@ -188,4 +191,4 @@ When you run a project on Visual Studio using IIS Express as the web server, the

## See Also

* [Video tutorial](https://abp.io/video-courses/essentials/generating-client-proxies)
* [Video tutorial](https://abp.io/video-courses/essentials/generating-client-proxies)
15 changes: 14 additions & 1 deletion npm/ng-packs/packages/core/src/lib/services/rest.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HttpClient, HttpHeaders, HttpParameterCodec, HttpParams, HttpRequest } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { Injectable, Signal, inject } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { Observable, from, of, throwError } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
import { ExternalHttpClient } from '../clients/http.client';
Expand Down Expand Up @@ -66,6 +67,18 @@ export class RestService {
}),
);
}

requestResource<T, R>(
request: Signal<Rest.Request<T>>,
config?: Rest.Config,
api?: string,
) {
return rxResource({
params: () => request(),
stream: ({ params }) => this.request<T, R>(params, config, api),
});
}

private getHttpClient(isExternal: boolean) {
return isExternal ? this.externalHttp : this.http;
}
Expand Down
16 changes: 16 additions & 0 deletions npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { effect, signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { createHttpFactory, HttpMethod, SpectatorHttp, SpyObject } from '@ngneat/spectator/vitest';
import { OAuthService } from 'angular-oauth2-oidc';
import { of, throwError } from 'rxjs';
Expand Down Expand Up @@ -92,6 +94,20 @@ describe('HttpClient testing', () => {
await completionPromise;
});

test('should create a resource-based request that still uses the ABP request pipeline', () => {
const resource = TestBed.runInInjectionContext(() => {
const resource = spectator.service.requestResource(signal({ method: HttpMethod.GET, url: '/test' }));
effect(() => {
resource.value();
});
return resource;
});

expect(typeof resource.reload).toBe('function');
expect(typeof resource.value).toBe('function');
expect(typeof resource.hasValue).toBe('function');
});

test('should handle the error', () => {
const spy = vi.spyOn(httpErrorReporter, 'reportError');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface GenerateProxyGeneratorSchema {
target: string;
url: string;
serviceType: string;
resourceApi: boolean;
entryPoint: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
]
}
},
"resourceApi": {
"description": "Generate Resource API helpers for read endpoints",
"type": "boolean",
"default": false
},
"entryPoint": {
"description": "Target Angular project to place the generated code",
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,45 @@ export class <%= name %>Service {
apiName = '<%= apiName %>';<%
for (let {body, signature} of methods) { %>
<%
const isBlob = body.isBlobMethod() ;
const responseType = isBlob ? "Blob":body.responseType;
const isBlob = body.isBlobMethod();
const responseType = isBlob ? 'Blob' : body.responseType;
const httpResponseType = body.httpResponseType;
const acceptHeader = body.acceptHeader;
const resourceParameters = signature.parameters.filter(p => p.name !== 'config');
const resourceType = resourceParameters.length
? `{ ${resourceParameters.map(p => `${p.name}${p.optional}: ${p.type}`).join('; ')} }`
: '';
const resourceArgs = resourceParameters.map(p => `params.${p.name}`).join(', ');
%>

<% if (resourceApi && body.method === 'GET') { %>
<%= camel(signature.name) %> = (<%
if (resourceParameters.length) { %>
params: Signal<<%= resourceType %>>,<% } %>
config?: Partial<Rest.Config>
): ResourceRef<<%= responseType %> | undefined> => this.restService.requestResource(
computed(() => {<%
if (resourceParameters.length) { %>
const { <%= resourceParameters.map(p => p.name).join(', ') %> } = params();<% } %>
return ({
method: '<%= body.method %>',<%
if (httpResponseType && httpResponseType !== 'json') { %>
responseType: '<%= httpResponseType %>',<% } %><%
if (acceptHeader) { %>
headers: { Accept: '<%= acceptHeader %>' },<% } %>
url: <%= body.url %>,<%
if (body.dictParamVar && !body.params.length) { %>
params: <%= body.dictParamVar %>,<% } %><%
if (body.dictParamVar && body.params.length) { %>
params: { ...<%= body.dictParamVar %>, <%= body.params.join(', ') %> },<% } %><%
if (!body.dictParamVar && body.params.length) { %>
params: { <%= body.params.join(', ') %> },<% }
if (body.body) { %>
body: <%= body.body %>,<% } %>
});
}),
{ apiName: this.apiName, ...config },
);<% } else { %>
<%= camel(signature.name) %> = (<%= serializeParameters(signature.parameters) %>) =>
this.restService.request<<%= body.requestType %>, <%= responseType %>>({
method: '<%= body.method %>',<%
Expand All @@ -32,5 +65,6 @@ export class <%= name %>Service {
if (body.body) { %>
body: <%= body.body %>,<% } %>
},
{ apiName: this.apiName,...config });<% } %>
}
{ apiName: this.apiName,...config });<% } %><%
} %>
}
5 changes: 4 additions & 1 deletion npm/ng-packs/packages/schematics/src/commands/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default function (schema: GenerateProxySchema) {
const types = data.types;
const modules = data.modules;
const serviceType = schema.serviceType || defaultEServiceType;
const resourceApi = schema.resourceApi ?? false;

if (!types || !modules) {
throw new SchematicsException(Exception.InvalidApiDefinition);
Expand All @@ -82,6 +83,7 @@ export default function (schema: GenerateProxySchema) {
apiName,
controllers,
serviceImports,
resourceApi,
});

const modelImports: Record<string, string[]> = {};
Expand Down Expand Up @@ -177,7 +179,7 @@ function createModelGenerator(params: ModelGeneratorParams) {
}

function createServiceGenerator(params: ServiceGeneratorParams) {
const { targetPath, controllers, serviceImports } = params;
const { targetPath, controllers, serviceImports, resourceApi } = params;
const mapControllerToService = createControllerToServiceMapper(params);

return chain(
Expand All @@ -195,6 +197,7 @@ function createServiceGenerator(params: ServiceGeneratorParams) {
applyTemplates({
...cases,
serializeParameters,
resourceApi,
...service,
}),
move(normalize(targetPath)),
Expand Down
5 changes: 5 additions & 0 deletions npm/ng-packs/packages/schematics/src/commands/api/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
}
]
}
},
"resourceApi": {
"description": "Generate Resource API helpers for read endpoints",
"type": "boolean",
"default": false
}

},
Expand Down
11 changes: 8 additions & 3 deletions npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
createProxyWarningSaver,
mergeAndAllowDelete,
removeDefaultPlaceholders,
resolveProxyResourceApi,
resolveProject,
} from '../../utils';

Expand All @@ -24,17 +25,21 @@ export default function (schema: GenerateProxySchema) {
const targetPath = buildTargetPath(target.definition, params.entryPoint);
const readProxyConfig = createProxyConfigReader(targetPath);
let generated: string[] = [];
let previousResourceApi = false;

try {
generated = readProxyConfig(host).generated;
const previousConfig = readProxyConfig(host);
generated = previousConfig.generated;
previousResourceApi = resolveProxyResourceApi(params, previousConfig);
const index = generated.findIndex(m => m === moduleName);
if (index < 0) generated.push(moduleName);
} catch (_) {
generated.push(moduleName);
previousResourceApi = resolveProxyResourceApi(params);
}

const getApiDefinition = createApiDefinitionGetter(params);
const data = { generated, ...(await getApiDefinition(host)) };
const data = { generated, resourceApi: previousResourceApi, ...(await getApiDefinition(host)) };
data.generated = [];

const clearProxy = createProxyClearer(targetPath);
Expand All @@ -43,7 +48,7 @@ export default function (schema: GenerateProxySchema) {

const saveProxyWarning = createProxyWarningSaver(targetPath);

const generateApis = createApisGenerator(schema, generated);
const generateApis = createApisGenerator({ ...schema, resourceApi: previousResourceApi }, generated);

const generateIndex = createProxyIndexGenerator(targetPath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
]
}
},
"resourceApi": {
"description": "Generate Resource API helpers for read endpoints",
"type": "boolean",
"default": false
},
"entryPoint": {
"description": "Target Angular project to place the generated code",
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createProxyIndexGenerator,
mergeAndAllowDelete,
removeDefaultPlaceholders,
resolveProxyResourceApi,
resolveProject,
} from '../../utils';

Expand All @@ -21,17 +22,19 @@ export default function (schema: GenerateProxySchema) {
const targetPath = buildTargetPath(target.definition, params.entryPoint);

const readProxyConfig = createProxyConfigReader(targetPath);
const { generated } = readProxyConfig(host);
const previousConfig = readProxyConfig(host);
const { generated } = previousConfig;
const resourceApi = resolveProxyResourceApi(params, previousConfig);

const getApiDefinition = createApiDefinitionGetter(params);
const data = { generated, ...(await getApiDefinition(host)) };
const data = { generated, resourceApi, ...(await getApiDefinition(host)) };
data.generated = [];

const clearProxy = createProxyClearer(targetPath);

const saveProxyConfig = createProxyConfigSaver(data, targetPath);

const generateApis = createApisGenerator(schema, generated);
const generateApis = createApisGenerator({ ...schema, resourceApi }, generated);

const generateIndex = createProxyIndexGenerator(targetPath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
},
"x-prompt": "Please enter URL for api definition (default: API Name's url in environment file)"
},
"resourceApi": {
"description": "Generate Resource API helpers for read endpoints",
"type": "boolean",
"default": false
},
"entryPoint": {
"description": "Target Angular project to place the generated code",
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createProxyIndexGenerator,
mergeAndAllowDelete,
removeDefaultPlaceholders,
resolveProxyResourceApi,
resolveProject,
} from '../../utils';

Expand All @@ -23,21 +24,23 @@ export default function (schema: GenerateProxySchema) {
const targetPath = buildTargetPath(target.definition, params.entryPoint);

const readProxyConfig = createProxyConfigReader(targetPath);
const { generated } = readProxyConfig(host);
const previousConfig = readProxyConfig(host);
const { generated } = previousConfig;
const resourceApi = resolveProxyResourceApi(params, previousConfig);

const index = generated.findIndex(m => m === moduleName);
if (index < 0) return host;
generated.splice(index, 1);

const getApiDefinition = createApiDefinitionGetter(params);
const data = { generated, ...(await getApiDefinition(host)) };
const data = { generated, resourceApi, ...(await getApiDefinition(host)) };
data.generated = [];

const clearProxy = createProxyClearer(targetPath);

const saveProxyConfig = createProxyConfigSaver(data, targetPath);

const generateApis = createApisGenerator(schema, generated);
const generateApis = createApisGenerator({ ...schema, resourceApi }, generated);

const generateIndex = createProxyIndexGenerator(targetPath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
},
"x-prompt": "Please enter URL for API definition (default: API Name's url in environment file)"
},
"resourceApi": {
"description": "Generate Resource API helpers for read endpoints",
"type": "boolean",
"default": false
},
"entryPoint": {
"description": "Target Angular project to place the generated code",
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ export interface GenerateProxySchema {
*/
entryPoint?: string;
serviceType?: eServiceType;

/**
* Generate Resource API helpers for read operations.
*/
resourceApi?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { ApiDefinition } from './api-definition';

export interface ProxyConfig extends ApiDefinition {
generated: string[];
resourceApi?: boolean;
}
1 change: 1 addition & 0 deletions npm/ng-packs/packages/schematics/src/models/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ServiceGeneratorParams {
apiName: string;
controllers: Controller[];
serviceImports: Record<string, string[]>;
resourceApi?: boolean;
}

export class Service {
Expand Down
Loading
Loading