Browse Source

Move REST API call rule node to new UI

pull/14540/head
Maksym Tsymbarov 8 months ago
parent
commit
4a03ed31ce
  1. 114
      ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html
  2. 78
      ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html
  3. 4
      ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts
  4. 227
      ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html
  5. 30
      ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.scss
  6. 28
      ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts
  7. 9
      ui-ngx/src/assets/locale/locale.constant-en_US.json

114
ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<section [formGroup]="credentialsConfigFormGroup" class="flex flex-col"> <section [formGroup]="credentialsConfigFormGroup" class="tb-form-panel stroked">
<mat-expansion-panel class="tb-credentials-config-panel-group" expanded="true"> <mat-expansion-panel class="tb-credentials-config-panel-group tb-settings" expanded="true">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title translate>rule-node-config.credentials</mat-panel-title> <mat-panel-title translate>rule-node-config.credentials</mat-panel-title>
<mat-panel-description> <mat-panel-description>
@ -24,7 +24,7 @@
</mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>rule-node-config.credentials-type</mat-label> <mat-label translate>rule-node-config.credentials-type</mat-label>
<mat-select formControlName="type" required> <mat-select formControlName="type" required>
<mat-option *ngFor="let credentialsType of allCredentialsTypes" [value]="credentialsType" <mat-option *ngFor="let credentialsType of allCredentialsTypes" [value]="credentialsType"
@ -36,60 +36,60 @@
{{ 'rule-node-config.credentials-type-required' | translate }} {{ 'rule-node-config.credentials-type-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<section class="flex flex-col" [ngSwitch]="credentialsConfigFormGroup.get('type').value"> @if (credentialsConfigFormGroup.get('type').value !== 'anonymous') {
<ng-template ngSwitchCase="anonymous"> <section class="flex flex-col" [ngSwitch]="credentialsConfigFormGroup.get('type').value">
</ng-template> <ng-template ngSwitchCase="basic">
<ng-template ngSwitchCase="basic"> <mat-form-field class="mat-block">
<mat-form-field class="mat-block"> <mat-label translate>rule-node-config.username</mat-label>
<mat-label translate>rule-node-config.username</mat-label> <input required matInput formControlName="username">
<input required matInput formControlName="username"> <mat-error *ngIf="credentialsConfigFormGroup.get('username').hasError('required')">
<mat-error *ngIf="credentialsConfigFormGroup.get('username').hasError('required')"> {{ 'rule-node-config.username-required' | translate }}
{{ 'rule-node-config.username-required' | translate }} </mat-error>
</mat-error> </mat-form-field>
</mat-form-field> <mat-form-field class="mat-block">
<mat-form-field class="mat-block"> <mat-label translate>rule-node-config.password</mat-label>
<mat-label translate>rule-node-config.password</mat-label> <input type="password" [required]="passwordFieldRequired" matInput formControlName="password" autocomplete="new-password">
<input type="password" [required]="passwordFieldRequired" matInput formControlName="password" autocomplete="new-password"> <tb-toggle-password matSuffix></tb-toggle-password>
<tb-toggle-password matSuffix></tb-toggle-password> <mat-error *ngIf="credentialsConfigFormGroup.get('password').hasError('required')">
<mat-error *ngIf="credentialsConfigFormGroup.get('password').hasError('required')"> {{ 'rule-node-config.password-required' | translate }}
{{ 'rule-node-config.password-required' | translate }} </mat-error>
</mat-error> </mat-form-field>
</mat-form-field> </ng-template>
</ng-template> <ng-template ngSwitchCase="cert.PEM">
<ng-template ngSwitchCase="cert.PEM"> <div class="tb-form-hint tb-primary-fill mb-4">{{ 'rule-node-config.credentials-pem-hint' | translate }}</div>
<div class="tb-hint">{{ 'rule-node-config.credentials-pem-hint' | translate }}</div> <tb-file-input formControlName="caCert"
<tb-file-input formControlName="caCert" inputId="caCertSelect"
inputId="caCertSelect" [existingFileName]="credentialsConfigFormGroup.get('caCertFileName').value"
[existingFileName]="credentialsConfigFormGroup.get('caCertFileName').value" (fileNameChanged)="credentialsConfigFormGroup.get('caCertFileName').setValue($event)"
(fileNameChanged)="credentialsConfigFormGroup.get('caCertFileName').setValue($event)" label="{{'rule-node-config.ca-cert' | translate}}"
label="{{'rule-node-config.ca-cert' | translate}}" noFileText="rule-node-config.no-file"
noFileText="rule-node-config.no-file" dropLabel="{{'rule-node-config.drop-file' | translate}}">
dropLabel="{{'rule-node-config.drop-file' | translate}}"> </tb-file-input>
</tb-file-input> <tb-file-input formControlName="cert"
<tb-file-input formControlName="cert" inputId="CertSelect"
inputId="CertSelect" [existingFileName]="credentialsConfigFormGroup.get('certFileName').value"
[existingFileName]="credentialsConfigFormGroup.get('certFileName').value" (fileNameChanged)="credentialsConfigFormGroup.get('certFileName').setValue($event)"
(fileNameChanged)="credentialsConfigFormGroup.get('certFileName').setValue($event)" label="{{'rule-node-config.cert' | translate}}"
label="{{'rule-node-config.cert' | translate}}" noFileText="rule-node-config.no-file"
noFileText="rule-node-config.no-file" dropLabel="{{'rule-node-config.drop-file' | translate}}">
dropLabel="{{'rule-node-config.drop-file' | translate}}"> </tb-file-input>
</tb-file-input> <tb-file-input style="padding-bottom: 8px;"
<tb-file-input style="padding-bottom: 8px;" formControlName="privateKey"
formControlName="privateKey" inputId="privateKeySelect"
inputId="privateKeySelect" [existingFileName]="credentialsConfigFormGroup.get('privateKeyFileName').value"
[existingFileName]="credentialsConfigFormGroup.get('privateKeyFileName').value" (fileNameChanged)="credentialsConfigFormGroup.get('privateKeyFileName').setValue($event)"
(fileNameChanged)="credentialsConfigFormGroup.get('privateKeyFileName').setValue($event)" label="{{'rule-node-config.private-key' | translate}}"
label="{{'rule-node-config.private-key' | translate}}" noFileText="rule-node-config.no-file"
noFileText="rule-node-config.no-file" dropLabel="{{'rule-node-config.drop-file' | translate}}">
dropLabel="{{'rule-node-config.drop-file' | translate}}"> </tb-file-input>
</tb-file-input> <mat-form-field class="mat-block">
<mat-form-field class="mat-block"> <mat-label translate>rule-node-config.private-key-password</mat-label>
<mat-label translate>rule-node-config.private-key-password</mat-label> <input type="password" matInput formControlName="password" autocomplete="new-password">
<input type="password" matInput formControlName="password" autocomplete="new-password"> <tb-toggle-password matSuffix></tb-toggle-password>
<tb-toggle-password matSuffix></tb-toggle-password> </mat-form-field>
</mat-form-field> </ng-template>
</ng-template> </section>
</section> }
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
</section> </section>

78
ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html

@ -16,40 +16,52 @@
--> -->
<section class="tb-kv-map-config flex flex-col" [formGroup]="kvListFormGroup"> <section class="tb-kv-map-config flex flex-col" [formGroup]="kvListFormGroup">
<div class="header flex flex-1 flex-row gap-2"> @if (description) {
<span class="cell tb-required flex-1">{{ keyText | translate }}</span> <div class="tb-form-hint tb-primary-fill mb-4" [innerHTML]="description | translate | safe: 'html'"></div>
<span class="cell tb-required flex-1">{{ valText | translate }}</span> }
<span [class.!hidden]="disabled" style="width: 52px;" innerHTML="&nbsp"></span> @if (keyValsFormArray().controls.length || !emptyText) {
</div> <div class="header flex flex-1 flex-row gap-2" >
<div class="body"> <span class="cell tb-required flex-1">{{ keyText | translate }}</span>
<div class="row flex flex-row items-center justify-start gap-2" <span class="cell tb-required flex-1">{{ valText | translate }}</span>
formArrayName="keyVals" <span [class.!hidden]="disabled" style="width: 52px;" innerHTML="&nbsp"></span>
*ngFor="let keyValControl of keyValsFormArray().controls; let $index = index">
<mat-form-field class="cell mat-block flex-1">
<input [formControl]="keyValControl.get('key')" matInput required
placeholder="{{ keyText | translate }}"/>
<mat-error *ngIf="keyValControl.get('key').hasError('required')">
{{ keyRequiredText | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="cell mat-block flex-1">
<input [formControl]="keyValControl.get('value')" matInput required
placeholder="{{ valText | translate }}"/>
<mat-error *ngIf="keyValControl.get('value').hasError('required')">
{{ valRequiredText | translate }}
</mat-error>
</mat-form-field>
<button mat-icon-button color="primary"
[class.!hidden]="disabled"
type="button"
(click)="removeKeyVal($index)"
[disabled]="isLoading$ | async"
matTooltip="{{ 'rule-node-config.key-val.remove-entry' | translate }}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</div> </div>
<div *ngIf="hintText" class="tb-hint" [innerHTML]="hintText | translate | safe: 'html'"></div> }
<div class="body">
@for (keyValControl of keyValsFormArray().controls; track $index) {
<div class="row flex flex-row items-center justify-start gap-2"
formArrayName="keyVals">
<mat-form-field class="cell mat-block flex-1">
<input [formControl]="keyValControl.get('key')" matInput required
placeholder="{{ keyText | translate }}"/>
<mat-error *ngIf="keyValControl.get('key').hasError('required')">
{{ keyRequiredText | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="cell mat-block flex-1">
<input [formControl]="keyValControl.get('value')" matInput required
placeholder="{{ valText | translate }}"/>
<mat-error *ngIf="keyValControl.get('value').hasError('required')">
{{ valRequiredText | translate }}
</mat-error>
</mat-form-field>
<button mat-icon-button color="primary"
[class.!hidden]="disabled"
type="button"
(click)="removeKeyVal($index)"
[disabled]="isLoading$ | async"
matTooltip="{{ 'rule-node-config.key-val.remove-entry' | translate }}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</div>
} @empty {
@if (emptyText) {
<div class="tb-prompt h-10 flex items-center justify-center">{{emptyText | translate}}</div>
}
}
@if (hintText) {
<div class="tb-hint" [innerHTML]="hintText | translate | safe: 'html'"></div>
}
</div> </div>
<tb-error [error]="ngControl.hasError('kvMapRequired') || ngControl.hasError('uniqueKeyValuePair') ? <tb-error [error]="ngControl.hasError('kvMapRequired') || ngControl.hasError('uniqueKeyValuePair') ?
(ngControl.hasError('kvMapRequired') ? translate.instant(requiredText) : (ngControl.hasError('kvMapRequired') ? translate.instant(requiredText) :

4
ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts

@ -67,6 +67,10 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal
@Input() hintText: string; @Input() hintText: string;
@Input() description: string;
@Input() emptyText: string;
private requiredValue: boolean; private requiredValue: boolean;
get required(): boolean { get required(): boolean {

227
ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html

@ -15,15 +15,34 @@
limitations under the License. limitations under the License.
--> -->
<section [formGroup]="restApiCallConfigForm" class="flex flex-col"> <section [formGroup]="restApiCallConfigForm" class="flex flex-col gap-4">
<mat-form-field class="mat-block" subscriptSizing="dynamic"> <div class="tb-form-panel stroked">
<mat-label translate>rule-node-config.endpoint-url-pattern</mat-label> <div class="tb-form-panel-title">Request URL</div>
<input required matInput formControlName="restEndpointUrlPattern"> <mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-error *ngIf="restApiCallConfigForm.get('restEndpointUrlPattern').hasError('required')"> <mat-label translate>rule-node-config.endpoint-url-pattern</mat-label>
{{ 'rule-node-config.endpoint-url-pattern-required' | translate }} <input required matInput formControlName="restEndpointUrlPattern">
</mat-error> <mat-error *ngIf="restApiCallConfigForm.get('restEndpointUrlPattern').hasError('required')">
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint> {{ 'rule-node-config.endpoint-url-pattern-required' | translate }}
</mat-form-field> </mat-error>
<mat-hint translate>rule-node-config.url-hint</mat-hint>
</mat-form-field>
<mat-expansion-panel class="tb-settings tb-form-panel stroked no-gap" [expanded]="isQueryParamsExpanded">
<mat-expansion-panel-header>
{{ 'rule-node-config.query-params' | translate }}
<div [tb-hint-tooltip-icon]="'rule-node-config.query-params-hint' | translate" tooltipPosition="above"></div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-kv-map-config-old
formControlName="queryParams"
keyText="rule-node-config.query-params-name"
keyRequiredText="rule-node-config.query-params-name-required"
valText="rule-node-config.query-params-value"
valRequiredText="rule-node-config.query-params-value-required"
emptyText="rule-node-config.query-params-empty">>
</tb-kv-map-config-old>
</ng-template>
</mat-expansion-panel>
</div>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>rule-node-config.request-method</mat-label> <mat-label translate>rule-node-config.request-method</mat-label>
<mat-select formControlName="requestMethod"> <mat-select formControlName="requestMethod">
@ -32,99 +51,113 @@
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox formControlName="enableProxy"> <div class="tb-form-panel stroked">
{{ 'rule-node-config.enable-proxy' | translate }} <mat-expansion-panel class="tb-settings" [expanded]="isHeadersExpanded">
</mat-checkbox> <mat-expansion-panel-header>
<mat-checkbox *ngIf="!restApiCallConfigForm.get('enableProxy').value" formControlName="useSimpleClientHttpFactory"> {{ 'rule-node-config.headers' | translate }}
{{ 'rule-node-config.use-simple-client-http-factory' | translate }} </mat-expansion-panel-header>
</mat-checkbox> <ng-template matExpansionPanelContent>
<mat-checkbox formControlName="parseToPlainText"> <tb-kv-map-config-old
{{ 'rule-node-config.parse-to-plain-text' | translate }} formControlName="headers"
</mat-checkbox> keyText="rule-node-config.header"
<div class="tb-hint" style="padding-bottom: 5px;" translate>rule-node-config.parse-to-plain-text-hint</div> keyRequiredText="rule-node-config.header-required"
<mat-checkbox formControlName="ignoreRequestBody"> valText="rule-node-config.value"
{{ 'rule-node-config.ignore-request-body' | translate }} valRequiredText="rule-node-config.value-required"
</mat-checkbox> description="rule-node-config.headers-hint"
<div *ngIf="restApiCallConfigForm.get('enableProxy').value"> emptyText="rule-node-config.headers-empty">
<mat-checkbox formControlName="useSystemProxyProperties"> </tb-kv-map-config-old>
{{ 'rule-node-config.use-system-proxy-properties' | translate }} </ng-template>
</mat-checkbox> </mat-expansion-panel>
<div *ngIf="!restApiCallConfigForm.get('useSystemProxyProperties').value"> </div>
<div class="gt-sm:flex gt-sm:flex-row gt-sm:gap-2"> <tb-credentials-config formControlName="credentials"></tb-credentials-config>
<mat-form-field class="mat-block gt-sm:max-w-10% gt-sm:flex-full"> <div class="tb-form-panel stroked">
<mat-label translate>rule-node-config.proxy-scheme</mat-label> <mat-expansion-panel class="tb-settings">
<mat-select formControlName="proxyScheme"> <mat-expansion-panel-header>
<mat-option *ngFor="let proxyScheme of proxySchemes" [value]="proxyScheme"> {{ 'rule-node-config.advanced-settings' | translate }}
{{ proxyScheme }} </mat-expansion-panel-header>
</mat-option> <ng-template matExpansionPanelContent>
</mat-select> <div class="tb-form-panel stroked column">
</mat-form-field> <mat-slide-toggle class="mat-slide fixed-title-width" formControlName="enableProxy">
<mat-form-field class="md-block gt-sm:max-w-50% gt-sm:flex-full"> {{ 'rule-node-config.enable-proxy' | translate }}
<mat-label translate>rule-node-config.proxy-host</mat-label> </mat-slide-toggle>
<input matInput required formControlName="proxyHost"> <div *ngIf="restApiCallConfigForm.get('enableProxy').value" class="flex flex-col gap-4">
<mat-error *ngIf="restApiCallConfigForm.get('proxyHost').hasError('required')"> <mat-slide-toggle class="mat-slide fixed-title-width" formControlName="useSystemProxyProperties">
{{ 'rule-node-config.proxy-host-required' | translate }} {{ 'rule-node-config.use-system-proxy-properties' | translate }}
</mat-slide-toggle>
<div *ngIf="!restApiCallConfigForm.get('useSystemProxyProperties').value">
<div class="gt-sm:flex gt-sm:flex-row gt-sm:gap-2">
<mat-form-field class="md-block gt-sm:max-w-50% gt-sm:flex-full">
<mat-label translate>rule-node-config.proxy-host</mat-label>
<input matInput required formControlName="proxyHost">
<mat-error *ngIf="restApiCallConfigForm.get('proxyHost').hasError('required')">
{{ 'rule-node-config.proxy-host-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block gt-sm:max-w-50% gt-sm:flex-full">
<mat-label translate>rule-node-config.proxy-port</mat-label>
<input matInput required formControlName="proxyPort" type="number" step="1">
<mat-error *ngIf="restApiCallConfigForm.get('proxyPort').hasError('required')">
{{ 'rule-node-config.proxy-port-required' | translate }}
</mat-error>
<mat-error
*ngIf="restApiCallConfigForm.get('proxyPort').hasError('min') || restApiCallConfigForm.get('proxyPort').hasError('max')">
{{ 'rule-node-config.proxy-port-range' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block">
<mat-label translate>rule-node-config.proxy-user</mat-label>
<input matInput formControlName="proxyUser">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>rule-node-config.proxy-password</mat-label>
<input matInput type="password" formControlName="proxyPassword">
<tb-toggle-password matSuffix></tb-toggle-password>
</mat-form-field>
</div>
</div>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="parseToPlainText">
<div [tb-hint-tooltip-icon]="'rule-node-config.parse-to-plain-text-hint' | translate" tooltipPosition="above">
{{ 'rule-node-config.parse-to-plain-text' | translate }}
</div>
</mat-slide-toggle>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="ignoreRequestBody">
{{ 'rule-node-config.ignore-request-body' | translate }}
</mat-slide-toggle>
</div>
<mat-form-field *ngIf="restApiCallConfigForm.get('enableProxy').value" class="mat-block"
subscriptSizing="dynamic">
<mat-label translate>rule-node-config.read-timeout</mat-label>
<input type="text" min="0" [max]="IntLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="readTimeoutMs">
<mat-hint translate>rule-node-config.read-timeout-hint</mat-hint>
<mat-error
*ngIf="restApiCallConfigForm.get('readTimeoutMs').hasError('max')">
{{ 'rule-node-config.int-range' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block gt-sm:max-w-40% gt-sm:flex-full"> <mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>rule-node-config.proxy-port</mat-label> <mat-label translate>rule-node-config.max-parallel-requests-count</mat-label>
<input matInput required formControlName="proxyPort" type="number" step="1"> <input type="text" min="0" [max]="IntLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="maxParallelRequestsCount">
<mat-error *ngIf="restApiCallConfigForm.get('proxyPort').hasError('required')"> <mat-hint translate>rule-node-config.max-parallel-requests-count-hint</mat-hint>
{{ 'rule-node-config.proxy-port-required' | translate }} <mat-error
*ngIf="restApiCallConfigForm.get('maxParallelRequestsCount').hasError('max')">
{{ 'rule-node-config.int-range' | translate }}
</mat-error> </mat-error>
</mat-form-field>
<mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>rule-node-config.max-response-size</mat-label>
<input type="text" min="1" [max]="MemoryBufferSizeInKbLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="maxInMemoryBufferSizeInKb">
<mat-hint translate>rule-node-config.max-response-size-hint</mat-hint>
<mat-error <mat-error
*ngIf="restApiCallConfigForm.get('proxyPort').hasError('min') || restApiCallConfigForm.get('proxyPort').hasError('max')"> *ngIf="restApiCallConfigForm.get('maxInMemoryBufferSizeInKb').hasError('min') || restApiCallConfigForm.get('maxInMemoryBufferSizeInKb').hasError('max')">
{{ 'rule-node-config.proxy-port-range' | translate }} {{ 'rule-node-config.memory-buffer-size-range' | translate: { max: MemoryBufferSizeInKbLimit } }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </ng-template>
<mat-form-field class="mat-block"> </mat-expansion-panel>
<mat-label translate>rule-node-config.proxy-user</mat-label>
<input matInput formControlName="proxyUser">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>rule-node-config.proxy-password</mat-label>
<input matInput formControlName="proxyPassword">
</mat-form-field>
</div>
</div> </div>
<mat-form-field *ngIf="!restApiCallConfigForm.get('useSimpleClientHttpFactory').value || restApiCallConfigForm.get('enableProxy').value" class="mat-block"
subscriptSizing="dynamic">
<mat-label translate>rule-node-config.read-timeout</mat-label>
<input type="text" min="0" [max]="IntLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="readTimeoutMs">
<mat-hint translate>rule-node-config.read-timeout-hint</mat-hint>
<mat-error
*ngIf="restApiCallConfigForm.get('readTimeoutMs').hasError('max')">
{{ 'rule-node-config.int-range' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>rule-node-config.max-parallel-requests-count</mat-label>
<input type="text" min="0" [max]="IntLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="maxParallelRequestsCount">
<mat-hint translate>rule-node-config.max-parallel-requests-count-hint</mat-hint>
<mat-error
*ngIf="restApiCallConfigForm.get('maxParallelRequestsCount').hasError('max')">
{{ 'rule-node-config.int-range' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>rule-node-config.max-response-size</mat-label>
<input type="text" min="1" [max]="MemoryBufferSizeInKbLimit" inputmode="numeric" pattern="[0-9]*" matInput formControlName="maxInMemoryBufferSizeInKb">
<mat-hint translate>rule-node-config.max-response-size-hint</mat-hint>
<mat-error
*ngIf="restApiCallConfigForm.get('maxInMemoryBufferSizeInKb').hasError('min') || restApiCallConfigForm.get('maxInMemoryBufferSizeInKb').hasError('max')">
{{ 'rule-node-config.memory-buffer-size-range' | translate: { max: MemoryBufferSizeInKbLimit } }}
</mat-error>
</mat-form-field>
<label translate class="tb-title">rule-node-config.headers</label>
<div class="tb-hint" [innerHTML]="'rule-node-config.headers-hint' | translate | safe: 'html'"> </div>
<tb-kv-map-config-old
required="false"
formControlName="headers"
keyText="rule-node-config.header"
keyRequiredText="rule-node-config.header-required"
valText="rule-node-config.value"
valRequiredText="rule-node-config.value-required">
</tb-kv-map-config-old>
<tb-credentials-config formControlName="credentials" [disableCertPemCredentials]="restApiCallConfigForm.get('useSimpleClientHttpFactory').value"></tb-credentials-config>
</section> </section>

30
ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.scss

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host ::ng-deep {
.mat-expansion-panel-content {
.mat-expansion-panel-body {
.tb-kv-map-config {
margin-bottom: 0;
.body {
.mdc-icon-button {
margin-bottom: 22px;
}
}
}
}
}
}

28
ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts

@ -18,21 +18,24 @@ import { Component } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models';
import { HttpRequestType, IntLimit } from '../rule-node-config.models'; import { HttpRequestType, IntLimit } from '../rule-node-config.models';
import { isDefinedAndNotNull } from '@core/utils';
@Component({ @Component({
selector: 'tb-external-node-rest-api-call-config', selector: 'tb-external-node-rest-api-call-config',
templateUrl: './rest-api-call-config.component.html', templateUrl: './rest-api-call-config.component.html',
styleUrls: [] styleUrls: ['./rest-api-call-config.component.scss']
}) })
export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent { export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent {
restApiCallConfigForm: UntypedFormGroup; restApiCallConfigForm: UntypedFormGroup;
readonly proxySchemes: string[] = ['http', 'https'];
readonly httpRequestTypes = Object.keys(HttpRequestType); readonly httpRequestTypes = Object.keys(HttpRequestType);
readonly MemoryBufferSizeInKbLimit = 25000; readonly MemoryBufferSizeInKbLimit = 25000;
readonly IntLimit = IntLimit; readonly IntLimit = IntLimit;
isHeadersExpanded = false;
isQueryParamsExpanded = false;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder) {
super(); super();
} }
@ -45,12 +48,10 @@ export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent {
this.restApiCallConfigForm = this.fb.group({ this.restApiCallConfigForm = this.fb.group({
restEndpointUrlPattern: [configuration ? configuration.restEndpointUrlPattern : null, [Validators.required]], restEndpointUrlPattern: [configuration ? configuration.restEndpointUrlPattern : null, [Validators.required]],
requestMethod: [configuration ? configuration.requestMethod : null, [Validators.required]], requestMethod: [configuration ? configuration.requestMethod : null, [Validators.required]],
useSimpleClientHttpFactory: [configuration ? configuration.useSimpleClientHttpFactory : false, []],
parseToPlainText: [configuration ? configuration.parseToPlainText : false, []], parseToPlainText: [configuration ? configuration.parseToPlainText : false, []],
ignoreRequestBody: [configuration ? configuration.ignoreRequestBody : false, []], ignoreRequestBody: [configuration ? configuration.ignoreRequestBody : false, []],
enableProxy: [configuration ? configuration.enableProxy : false, []], enableProxy: [configuration ? configuration.enableProxy : false, []],
useSystemProxyProperties: [configuration ? configuration.enableProxy : false, []], useSystemProxyProperties: [configuration ? configuration.enableProxy : false, []],
proxyScheme: [configuration ? configuration.proxyHost : null, []],
proxyHost: [configuration ? configuration.proxyHost : null, []], proxyHost: [configuration ? configuration.proxyHost : null, []],
proxyPort: [configuration ? configuration.proxyPort : null, []], proxyPort: [configuration ? configuration.proxyPort : null, []],
proxyUser: [configuration ? configuration.proxyUser :null, []], proxyUser: [configuration ? configuration.proxyUser :null, []],
@ -59,16 +60,22 @@ export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent {
maxParallelRequestsCount: [configuration ? configuration.maxParallelRequestsCount : null, [Validators.min(0), Validators.max(IntLimit)]], maxParallelRequestsCount: [configuration ? configuration.maxParallelRequestsCount : null, [Validators.min(0), Validators.max(IntLimit)]],
headers: [configuration ? configuration.headers : null, []], headers: [configuration ? configuration.headers : null, []],
credentials: [configuration ? configuration.credentials : null, []], credentials: [configuration ? configuration.credentials : null, []],
maxInMemoryBufferSizeInKb: [configuration ? configuration.maxInMemoryBufferSizeInKb : null, [Validators.min(1), Validators.max(this.MemoryBufferSizeInKbLimit)]] maxInMemoryBufferSizeInKb: [configuration ? configuration.maxInMemoryBufferSizeInKb : null, [Validators.min(1), Validators.max(this.MemoryBufferSizeInKbLimit)]],
queryParams: [configuration ? configuration.queryParams : [], []],
}); });
} }
protected validatorTriggers(): string[] { protected validatorTriggers(): string[] {
return ['useSimpleClientHttpFactory', 'enableProxy', 'useSystemProxyProperties']; return ['enableProxy', 'useSystemProxyProperties'];
}
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
this.isQueryParamsExpanded = configuration && isDefinedAndNotNull(configuration.queryParams) && !!Object.keys(configuration.queryParams).length;
this.isHeadersExpanded = configuration && isDefinedAndNotNull(configuration.headers) && !!Object.keys(configuration.headers).length;
return super.prepareInputConfig(configuration);
} }
protected updateValidators(emitEvent: boolean) { protected updateValidators(emitEvent: boolean) {
const useSimpleClientHttpFactory: boolean = this.restApiCallConfigForm.get('useSimpleClientHttpFactory').value;
const enableProxy: boolean = this.restApiCallConfigForm.get('enableProxy').value; const enableProxy: boolean = this.restApiCallConfigForm.get('enableProxy').value;
const useSystemProxyProperties: boolean = this.restApiCallConfigForm.get('useSystemProxyProperties').value; const useSystemProxyProperties: boolean = this.restApiCallConfigForm.get('useSystemProxyProperties').value;
@ -79,12 +86,7 @@ export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent {
} else { } else {
this.restApiCallConfigForm.get('proxyHost').setValidators([]); this.restApiCallConfigForm.get('proxyHost').setValidators([]);
this.restApiCallConfigForm.get('proxyPort').setValidators([]); this.restApiCallConfigForm.get('proxyPort').setValidators([]);
this.restApiCallConfigForm.get('readTimeoutMs').setValidators([Validators.min(0), Validators.max(IntLimit)]);
if (useSimpleClientHttpFactory) {
this.restApiCallConfigForm.get('readTimeoutMs').setValidators([]);
} else {
this.restApiCallConfigForm.get('readTimeoutMs').setValidators([Validators.min(0), Validators.max(IntLimit)]);
}
} }
this.restApiCallConfigForm.get('readTimeoutMs').updateValueAndValidity({emitEvent}); this.restApiCallConfigForm.get('readTimeoutMs').updateValueAndValidity({emitEvent});

9
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -5404,6 +5404,15 @@
"tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.", "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.",
"basic-mode": "Basic", "basic-mode": "Basic",
"advanced-mode": "Advanced", "advanced-mode": "Advanced",
"url-hint" : "Enter the request URL. Ensure special characters are percent-encoded.",
"query-params": "Query parameters",
"query-params-hint": "Additional query parameters to include in the request URL. Both names and values support templatization and are strictly (RFC 3986) encoded after template resolution.",
"query-params-empty": "No query parameters configured",
"query-params-name": "Name",
"query-params-value": "Value",
"query-params-name-required": "Name is required",
"query-params-value-required": "Value is required",
"headers-empty": "No headers configured",
"save-time-series": { "save-time-series": {
"processing-settings": "Processing settings", "processing-settings": "Processing settings",
"processing-settings-hint": "Define how incoming messages are processed. Basic processing settings allow you to select preconfigured strategies, while Advanced settings allow you to select individual processing strategies for each action.", "processing-settings-hint": "Define how incoming messages are processed. Basic processing settings allow you to select preconfigured strategies, while Advanced settings allow you to select individual processing strategies for each action.",

Loading…
Cancel
Save