Skip to content

Commit

Permalink
fix: AddEndpointForm headers
Browse files Browse the repository at this point in the history
  • Loading branch information
ralfaron committed Oct 15, 2024
1 parent dc7f3ed commit 870ad0b
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 114 deletions.
2 changes: 1 addition & 1 deletion projects/aas-lib/src/lib/aas-tree/aas-tree-row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class TreeInitialize {
for (const shell of this.env.assetAdministrationShells) {
const row = this.createRow(shell, -1, 0, true);
this.rows.push(row);
row.firstChild = this.hasChildren(shell) ? this.rows.length : -1;
row.firstChild = this.env.submodels.length > 0 ? this.rows.length : -1;
this.traverse(shell, this.rows.length - 1, 1);
for (const stateRow of this.rows) {
if (stateRow.expanded || stateRow.selected) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,63 +28,51 @@ <h4 class="modal-title">
(input)="inputValue()">
<label class="form-lable" for="div-endpoint-url" translate>AddEndpointForm.LABEL_URL</label>
<div class="input-group" ngbDropdown role="group" id="div-endpoint-url">
<button type="button" class="btn btn-secondary" ngbDropdownToggle>{{'AddEndpointForm.' + item().type |
<button type="button" class="btn btn-secondary" ngbDropdownToggle>{{'AddEndpointForm.' + selectedItem().type
|
translate}}</button>
<div class="dropdown-menu" ngbDropdownMenu>
@for (item of items(); track item) {
<button type="button" (click)="setItem(item)" ngbDropdownItem>{{'AddEndpointForm.' + item.type |
<button type="button" (click)="selectItem(item)" ngbDropdownItem>{{'AddEndpointForm.' + item.type |
translate}}</button>
}
</div>
<input id="input-endpoint-url" type="text" class="form-control"
placeholder="{{item().placeholder | translate}}" [(ngModel)]="item().value" name="value"
placeholder="{{selectedItem().placeholder | translate}}" [(ngModel)]="selectedItem().value" name="value"
(input)="inputValue()">
</div>
@if (item().type === 'AAS_API') {
<div class="col-12" style="max-height: 200px; overflow-y: auto;">
@if (selectedItem().type === 'AAS_API') {
<div class="col-12 mt-2" style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-striped table-borderless">
<thead>
<th [style.width.px]="16"></th>
<th></th>
<th></th>
<th [style.width.px]="16"></th>
<tr>
<th scope="col" class="text-center" [style.width]="'32px'">#</th>
<th scope="col" class="text-center">Header</th>
<th scope="col" class="text-center">Value</th>
</tr>
</thead>
<tbody>
@for (header of headers(); track header.name) {
@for (header of headers(); track header.id) {
<tr>
<th scope="row" (click)="selectHeader(header)">{{header.id}}</th>
@if (header === selectedHeader()) {
<td>
<input type="checkbox" class="form-check-input" [checked]="header.selected"
[(ngModel)]="header.selected" name="headerSelected" />
</td>
<td>
@if (!header.selected) {
<div class="small">{{header.name}}</div>
} @else {
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.name"
placeholder="header name" name="headerName" />
}
</td>
<td>
@if (!header.selected) {
<div class="small">{{header.value}}</div>
} @else {
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.value"
placeholder="value" name="headerValue" />
}
</td>
<td>
@if (header === lastHeader()) {
<a class="text-primary" href="javascript:void(0)" (click)="addHeader()">
<i class="bi bi-plus"></i>
</a>
} @else {
<a class="text-danger" href="javascript:void(0)" (click)="removeHeader(header)">
<i class="bi bi-dash"></i>
</a>
}
} @else {
<td (click)="selectHeader(header)">
<div class="small">{{header.name}}</div>
</td>
</tr>
}
<td (click)="selectHeader(header)">
<div class="small">{{header.value}}</div>
</td>
}
}
</tbody>
</table>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,22 @@
*
*****************************************************************************/

import head from 'lodash-es/head';
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgbActiveModal, NgbDropdownModule, NgbToast } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { AASEndpoint, AASEndpointType, stringFormat } from 'aas-core';

export interface HeaderItem {
id: string;
name: string;
value: string;
selected: boolean;
}

export interface EndpointItem {
type: AASEndpointType;
value: string;
placeholder: string;
headers: HeaderItem[];
selected: boolean;
}

@Component({
Expand All @@ -38,37 +35,37 @@ export interface EndpointItem {
export class AddEndpointFormComponent {
private readonly endpoints = signal<AASEndpoint[]>([]);
private readonly _messages = signal<string[]>([]);
private _selectedItemIndex = signal(0);
private readonly _items = signal<EndpointItem[]>([
{
type: 'AAS_API',
value: '',
placeholder: 'AddEndpointForm.PLACEHOLDER_URL_HTTP',
headers: [{ name: '', value: '', selected: true }],
selected: true,
},
{
type: 'OPC_UA',
value: '',
placeholder: 'AddEndpointForm.PLACEHOLDER_URL_OPCUA',
headers: [{ name: '', value: '', selected: true }],
selected: false,
},
{
type: 'WebDAV',
value: '',
placeholder: 'AddEndpointForm.PLACEHOLDER_URL_WEBDAV',
headers: [{ name: '', value: '', selected: true }],
selected: false,
},
{
type: 'FileSystem',
value: '',
placeholder: 'AddEndpointForm.PLACEHOLDER_URL_FILE',
headers: [{ name: '', value: '', selected: true }],
selected: false,
},
]);

private readonly _selectedHeaderIndex = signal(-1);
private readonly _headers = signal<HeaderItem[]>([
{ id: '1', name: '', value: '' },
{ id: '2', name: '', value: '' },
{ id: '3', name: '', value: '' },
]);

public constructor(
private modal: NgbActiveModal,
private translate: TranslateService,
Expand All @@ -80,78 +77,60 @@ export class AddEndpointFormComponent {

public readonly items = this._items.asReadonly();

public readonly item = computed(() => this._items().find(item => item.selected)!);
public readonly selectedItem = computed(() => this._items()[this._selectedItemIndex()]);

public readonly headers = computed(() => this.item().headers);
public readonly headers = this._headers.asReadonly();

public readonly header = computed(() => this.item().headers.find(header => header.selected)!);

public readonly lastHeader = computed(() => head(this.headers())!);
public readonly selectedHeader = computed(() =>
this._selectedHeaderIndex() < 0 ? undefined : this._headers()[this._selectedHeaderIndex()],
);

public initialize(endpoints: AASEndpoint[]): void {
this.endpoints.set(endpoints);
}

public setItem(value: EndpointItem): void {
this._items.update(items =>
items.map(item => {
item.selected = item === value;
return item;
}),
);

public selectItem(value: EndpointItem): void {
this._selectedItemIndex.set(this._items().indexOf(value));
this.clearMessages();
}

public inputValue(): void {
this.clearMessages();
}
public selectHeader(value: HeaderItem): void {
this._selectedHeaderIndex.update(state => {
const index = this._headers().indexOf(value);
if (state >= 0 && index === state) {
return -1;
}

public addHeader(): void {
this._items.update(items =>
items.map(item => {
if (item.selected) {
const headers = item.headers.map(header => {
header.selected = false;
return header;
});

headers.push({ name: '', value: '', selected: true });
item.headers = headers;
}
return index;
});

return item;
}),
);
this.clearMessages();
}

public removeHeader(header: HeaderItem): void {
this._items.update(items =>
items.map(item => {
if (item.selected) {
item.headers = item.headers.filter(head => head !== header);
}

return item;
}),
);
public inputValue(): void {
this.clearMessages();
}

public submit(): void {
const selectedItem = this.selectedItem();
if (selectedItem === undefined) {
return;
}

this.clearMessages();
const name = this.validateName();
const url = this.validateUrl(this.item().value.trim());
const url = this.validateUrl(selectedItem.value.trim(), selectedItem.type);
if (name && url) {
const version = url.searchParams.get('version');
url.search = '';
const endpoint: AASEndpoint = { url: url.href, name, type: this.item().type };
const endpoint: AASEndpoint = { url: url.href, name, type: selectedItem.type };
if (version) {
endpoint.version = version;
} else if (this.item().type === 'AAS_API') {
} else if (selectedItem.type === 'AAS_API') {
endpoint.version = 'v3';
}

const headers = this.item().headers.filter(header => header.name && header.value);
const headers = this.headers().filter(header => header.name && header.value);
if (headers.length > 0) {
endpoint.headers = {};
headers.forEach(header => (endpoint.headers![header.name] = header.value));
Expand Down Expand Up @@ -192,10 +171,10 @@ export class AddEndpointFormComponent {
return name;
}

private validateUrl(value: string): URL | undefined {
private validateUrl(value: string, type: AASEndpointType): URL | undefined {
try {
const url = new URL(value);
switch (this.item().type) {
switch (type) {
case 'AAS_API':
this.validateAASApiEndpoint(url);
break;
Expand All @@ -212,10 +191,7 @@ export class AddEndpointFormComponent {

return url;
} catch (error) {
this._messages.update(messages => [
...messages,
this.createMessage('ERROR_INVALID_URL', this.item().value),
]);
this._messages.update(messages => [...messages, this.createMessage('ERROR_INVALID_URL', value)]);

return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { AASApiClientV3 } from '../packages/aas-server/aas-api-client-v3.js';
import { AASApiClientV1 } from '../packages/aas-server/aas-api-client-v1.js';
import { AASApiClientV0 } from '../packages/aas-server/aas-api-client-v0.js';
import { FileStorageProvider } from '../file-storage/file-storage-provider.js';
import { HttpClient } from '../packages/http-client.js';
import { HttpClient } from '../http-client.js';

@singleton()
export class AASResourceScanFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import http from 'http';
import https from 'https';
import net from 'net';
import FormData from 'form-data';
import { parseUrl } from '../convert.js';
import { parseUrl } from './convert.js';
import { singleton } from 'tsyringe';

@singleton()
Expand All @@ -22,20 +22,19 @@ export class HttpClient {
* @param headers Additional outgoing http headers.
* @returns The requested object.
*/
public get<T extends object>(url: URL, headers?: http.OutgoingHttpHeaders): Promise<T> {
public get<T extends object>(url: URL, headers: http.OutgoingHttpHeaders = {}): Promise<T> {
return new Promise((result, reject) => {
const options: http.RequestOptions = {
host: url.hostname,
port: url.port,
path: url.pathname + url.search,
method: 'GET',
timeout: 3000,
headers: {
connection: 'keep-alive',
...headers,
},
};

if (headers) {
options.headers = { ...headers };
}

const requester = url.protocol === 'https:' ? https.request : http.request;
const request = requester(options, response => {
let data = '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { OpcuaClient } from './opcua/opcua-client.js';
import { ERRORS } from '../errors.js';
import { FileStorageProvider } from '../file-storage/file-storage-provider.js';
import { AASApiClientV1 } from './aas-server/aas-api-client-v1.js';
import { HttpClient } from './http-client.js';
import { HttpClient } from '../http-client.js';

@singleton()
export class AASResourceFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { JsonReaderV2 } from '../json-reader-v2.js';
import { AASApiClient } from './aas-api-client.js';
import { JsonWriterV2 } from '../json-writer-v2.js';
import * as aasV2 from '../../types/aas-v2.js';
import { HttpClient } from '../http-client.js';
import { HttpClient } from '../../http-client.js';

interface AASList {
aaslist: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { JsonReaderV2 } from '../json-reader-v2.js';
import { JsonWriterV2 } from '../json-writer-v2.js';
import { ERRORS } from '../../errors.js';
import { JsonReaderV3 } from '../json-reader-v3.js';
import { HttpClient } from '../http-client.js';
import { HttpClient } from '../../http-client.js';

interface PackageDescriptor {
aasIds: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
isSubmodelElement,
selectSubmodel,
} from 'aas-core';
import { HttpClient } from '../http-client.js';
import { HttpClient } from '../../http-client.js';

interface PackageDescriptor {
aasIds: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*****************************************************************************/

import { aas, AASEndpoint, convertFromString, DefaultType, DifferenceItem, LiveRequest } from 'aas-core';
import { HttpClient } from '../http-client.js';
import { HttpClient } from '../../http-client.js';
import { Logger } from '../../logging/logger.js';
import { HttpSubscription } from '../../live/http/http-subscription.js';
import { SocketClient } from '../../live/socket-client.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import net from 'net';
import http, { IncomingMessage } from 'http';
import { Socket } from 'net';
import { HttpClient } from '../../app/packages/http-client.js';
import { HttpClient } from '../app/http-client.js';
import { createSpyObj } from 'fhg-jest';
import { describe, beforeEach, it, expect, jest, afterEach } from '@jest/globals';

Expand Down
Loading

0 comments on commit 870ad0b

Please sign in to comment.