Browse Source

fix: enhance remote environment configuration handling and error reporting

pull/24815/head
maliming 1 day ago
parent
commit
8c8fd73a83
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 74
      docs/en/framework/ui/angular/environment.md
  2. 22
      npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts
  3. 14
      npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts

74
docs/en/framework/ui/angular/environment.md

@ -108,6 +108,80 @@ export interface RemoteEnv {
- `method`: HTTP method to be used when retrieving environment config. Default: `GET`
- `headers`: If extra headers are needed for the request, it can be set through this field.
### Example RemoteEnv Configuration
To enable dynamic environment configuration at runtime, add the `remoteEnv` property to your environment file:
```ts
export const environment = {
// ... other configurations
remoteEnv: {
url: '/getEnvConfig',
mergeStrategy: 'deepmerge'
}
} as Environment;
```
### Web Server Configuration
When using `remoteEnv` with a URL like `/getEnvConfig`, you need to configure your web server to serve the `dynamic-env.json` file with the correct content type (`application/json`). Otherwise, the application may receive HTML (e.g., your `index.html`) instead of JSON, causing configuration to fail silently.
#### IIS Configuration (web.config)
```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect" stopProcessing="true">
<match url="getEnvConfig" />
<action type="Redirect" url="dynamic-env.json" />
</rule>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="./index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
```
#### Nginx Configuration
```nginx
server {
listen 80;
listen [::]:80;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
location /getEnvConfig {
default_type 'application/json';
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Content-Type' 'application/json';
root /usr/share/nginx/html;
try_files $uri /dynamic-env.json;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
```
## Configure Core Provider with Environment
`environment` variable comes from angular host application.

22
npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts

@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Component, Injector } from '@angular/core';
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, throwError } from 'rxjs';
import { Environment, RemoteEnv } from '../models/environment';
import { EnvironmentService } from '../services/environment.service';
import { getRemoteEnv } from '../utils/environment-utils';
@ -85,7 +85,6 @@ describe('EnvironmentUtils', () => {
injectorSpy.mockReturnValueOnce(environmentService);
injectorSpy.mockReturnValueOnce(http);
injectorSpy.mockReturnValueOnce({});
requestSpy.mockReturnValue(new BehaviorSubject(customEnv));
@ -95,5 +94,24 @@ describe('EnvironmentUtils', () => {
expect(requestSpy).toHaveBeenCalledWith('GET', '/assets/appsettings.json', { headers: {} });
expect(setStateSpy).toHaveBeenCalledWith(expectedValue);
}
it('should handle request error gracefully and use local environment', async () => {
const injector = spectator.inject(Injector);
const injectorSpy = jest.spyOn(injector, 'get');
const http = spectator.inject(HttpClient);
const requestSpy = jest.spyOn(http, 'request');
const environmentService = spectator.inject(EnvironmentService);
const setStateSpy = jest.spyOn(environmentService, 'setState');
injectorSpy.mockReturnValueOnce(environmentService);
injectorSpy.mockReturnValueOnce(http);
requestSpy.mockReturnValue(throwError(() => new Error('Network error')));
environment.remoteEnv.mergeStrategy = 'deepmerge';
await getRemoteEnv(injector, environment);
expect(setStateSpy).toHaveBeenCalledWith(deepMerge(environment, {}));
});
});
});

14
npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts

@ -1,10 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { Injector } from '@angular/core';
import { Injector, isDevMode } from '@angular/core';
import { of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { Environment, RemoteEnv } from '../models/environment';
import { EnvironmentService } from '../services/environment.service';
import { HttpErrorReporterService } from '../services/http-error-reporter.service';
import { deepMerge } from './object-utils';
export function getRemoteEnv(injector: Injector, environment: Partial<Environment>) {
@ -15,15 +14,20 @@ export function getRemoteEnv(injector: Injector, environment: Partial<Environmen
if (!url) return Promise.resolve();
const http = injector.get(HttpClient);
const httpErrorReporter = injector.get(HttpErrorReporterService);
return http
.request<Environment>(method, url, { headers })
.pipe(
catchError(err => {
httpErrorReporter.reportError(err);
if (isDevMode()) {
console.warn(
`[ABP Environment] Failed to fetch remote environment from "${url}". ` +
`Error: ${err.message || err}\n` +
`See https://abp.io/docs/latest/framework/ui/angular/environment#example-remoteenv-configuration for configuration details.`,
);
}
return of(null);
}), // TODO: Consider get handle function from a provider
}),
tap(env =>
environmentService.setState(
mergeEnvironments(environment, env || ({} as Environment), remoteEnv as RemoteEnv),

Loading…
Cancel
Save