Merge pull request #9861 from vvlladd28/bug/gateway-dashboard/download-file
Fixed download gateway launch file and refactoring downloads file
This commit is contained in:
		
						commit
						41f81b161c
					
				@ -28,13 +28,13 @@ import {
 | 
			
		||||
  DeviceInfo,
 | 
			
		||||
  DeviceInfoQuery,
 | 
			
		||||
  DeviceSearchQuery,
 | 
			
		||||
  PublishLaunchCommand,
 | 
			
		||||
  PublishTelemetryCommand
 | 
			
		||||
} from '@shared/models/device.models';
 | 
			
		||||
import { EntitySubtype } from '@shared/models/entity-type.models';
 | 
			
		||||
import { AuthService } from '@core/auth/auth.service';
 | 
			
		||||
import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models';
 | 
			
		||||
import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models';
 | 
			
		||||
import { ResourcesService } from '@core/services/resources.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -42,7 +42,8 @@ import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models';
 | 
			
		||||
export class DeviceService {
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private resourcesService: ResourcesService
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  public getDeviceInfosByQuery(deviceInfoQuery: DeviceInfoQuery, config?: RequestConfig): Observable<PageData<DeviceInfo>> {
 | 
			
		||||
@ -215,8 +216,7 @@ export class DeviceService {
 | 
			
		||||
    return this.http.get<PublishTelemetryCommand>(`/api/device-connectivity/${deviceId}`, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getDevicePublishLaunchCommands(deviceId: string, config?: RequestConfig): Observable<PublishLaunchCommand> {
 | 
			
		||||
    return this.http.get<PublishLaunchCommand>(`/api/device-connectivity/gateway-launch/${deviceId}`, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  public downloadGatewayDockerComposeFile(deviceId: string): Observable<any> {
 | 
			
		||||
    return this.resourcesService.downloadResource(`/api/device-connectivity/gateway-launch/${deviceId}/docker-compose/download`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import {
 | 
			
		||||
import { catchError, map, switchMap } from 'rxjs/operators';
 | 
			
		||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
 | 
			
		||||
import { blobToBase64 } from '@core/utils';
 | 
			
		||||
import { ResourcesService } from '@core/services/resources.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -37,7 +38,8 @@ import { blobToBase64 } from '@core/utils';
 | 
			
		||||
export class ImageService {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private sanitizer: DomSanitizer
 | 
			
		||||
    private sanitizer: DomSanitizer,
 | 
			
		||||
    private resourcesService: ResourcesService
 | 
			
		||||
  ) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -113,34 +115,7 @@ export class ImageService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public downloadImage(type: ImageResourceType, key: string): Observable<any> {
 | 
			
		||||
    return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, {
 | 
			
		||||
      responseType: 'arraybuffer',
 | 
			
		||||
      observe: 'response'
 | 
			
		||||
    }).pipe(
 | 
			
		||||
      map((response) => {
 | 
			
		||||
        const headers = response.headers;
 | 
			
		||||
        const filename = headers.get('x-filename');
 | 
			
		||||
        const contentType = headers.get('content-type');
 | 
			
		||||
        const linkElement = document.createElement('a');
 | 
			
		||||
        try {
 | 
			
		||||
          const blob = new Blob([response.body], {type: contentType});
 | 
			
		||||
          const url = URL.createObjectURL(blob);
 | 
			
		||||
          linkElement.setAttribute('href', url);
 | 
			
		||||
          linkElement.setAttribute('download', filename);
 | 
			
		||||
          const clickEvent = new MouseEvent('click',
 | 
			
		||||
            {
 | 
			
		||||
              view: window,
 | 
			
		||||
              bubbles: true,
 | 
			
		||||
              cancelable: false
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
          linkElement.dispatchEvent(clickEvent);
 | 
			
		||||
          return null;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          throw e;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    return this.resourcesService.downloadResource(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public deleteImage(type: ImageResourceType, key: string, force = false, config?: RequestConfig) {
 | 
			
		||||
 | 
			
		||||
@ -27,12 +27,13 @@ import {
 | 
			
		||||
  OtaPagesIds,
 | 
			
		||||
  OtaUpdateType
 | 
			
		||||
} from '@shared/models/ota-package.models';
 | 
			
		||||
import { catchError, map, mergeMap } from 'rxjs/operators';
 | 
			
		||||
import { catchError, mergeMap } from 'rxjs/operators';
 | 
			
		||||
import { deepClone } from '@core/utils';
 | 
			
		||||
import { BaseData } from '@shared/models/base-data';
 | 
			
		||||
import { EntityId } from '@shared/models/id/entity-id';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { DialogService } from '@core/services/dialog.service';
 | 
			
		||||
import { ResourcesService } from '@core/services/resources.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -41,7 +42,8 @@ export class OtaPackageService {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private translate: TranslateService,
 | 
			
		||||
    private dialogService: DialogService
 | 
			
		||||
    private dialogService: DialogService,
 | 
			
		||||
    private resourcesService: ResourcesService
 | 
			
		||||
  ) {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
@ -65,31 +67,7 @@ export class OtaPackageService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public downloadOtaPackage(otaPackageId: string): Observable<any> {
 | 
			
		||||
    return this.http.get(`/api/otaPackage/${otaPackageId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe(
 | 
			
		||||
      map((response) => {
 | 
			
		||||
        const headers = response.headers;
 | 
			
		||||
        const filename = headers.get('x-filename');
 | 
			
		||||
        const contentType = headers.get('content-type');
 | 
			
		||||
        const linkElement = document.createElement('a');
 | 
			
		||||
        try {
 | 
			
		||||
          const blob = new Blob([response.body], { type: contentType });
 | 
			
		||||
          const url = URL.createObjectURL(blob);
 | 
			
		||||
          linkElement.setAttribute('href', url);
 | 
			
		||||
          linkElement.setAttribute('download', filename);
 | 
			
		||||
          const clickEvent = new MouseEvent('click',
 | 
			
		||||
            {
 | 
			
		||||
              view: window,
 | 
			
		||||
              bubbles: true,
 | 
			
		||||
              cancelable: false
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
          linkElement.dispatchEvent(clickEvent);
 | 
			
		||||
          return null;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          throw e;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    return this.resourcesService.downloadResource(`/api/otaPackage/${otaPackageId}/download`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public saveOtaPackage(otaPackage: OtaPackage, config?: RequestConfig): Observable<OtaPackage> {
 | 
			
		||||
 | 
			
		||||
@ -21,15 +21,17 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-uti
 | 
			
		||||
import { forkJoin, Observable, of } from 'rxjs';
 | 
			
		||||
import { PageData } from '@shared/models/page/page-data';
 | 
			
		||||
import { Resource, ResourceInfo, ResourceType } from '@shared/models/resource.models';
 | 
			
		||||
import { catchError, map, mergeMap } from 'rxjs/operators';
 | 
			
		||||
import { catchError, mergeMap } from 'rxjs/operators';
 | 
			
		||||
import { isNotEmptyStr } from '@core/utils';
 | 
			
		||||
import { ResourcesService } from '@core/services/resources.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class ResourceService {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private resourcesService: ResourcesService
 | 
			
		||||
  ) {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
@ -55,34 +57,7 @@ export class ResourceService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public downloadResource(resourceId: string): Observable<any> {
 | 
			
		||||
    return this.http.get(`/api/resource/${resourceId}/download`, {
 | 
			
		||||
      responseType: 'arraybuffer',
 | 
			
		||||
      observe: 'response'
 | 
			
		||||
    }).pipe(
 | 
			
		||||
      map((response) => {
 | 
			
		||||
        const headers = response.headers;
 | 
			
		||||
        const filename = headers.get('x-filename');
 | 
			
		||||
        const contentType = headers.get('content-type');
 | 
			
		||||
        const linkElement = document.createElement('a');
 | 
			
		||||
        try {
 | 
			
		||||
          const blob = new Blob([response.body], {type: contentType});
 | 
			
		||||
          const url = URL.createObjectURL(blob);
 | 
			
		||||
          linkElement.setAttribute('href', url);
 | 
			
		||||
          linkElement.setAttribute('download', filename);
 | 
			
		||||
          const clickEvent = new MouseEvent('click',
 | 
			
		||||
            {
 | 
			
		||||
              view: window,
 | 
			
		||||
              bubbles: true,
 | 
			
		||||
              cancelable: false
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
          linkElement.dispatchEvent(clickEvent);
 | 
			
		||||
          return null;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          throw e;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    return this.resourcesService.downloadResource(`/api/resource/${resourceId}/download`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public saveResources(resources: Resource[], config?: RequestConfig): Observable<Resource[]> {
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ import { AuthService } from '@core/auth/auth.service';
 | 
			
		||||
import { select, Store } from '@ngrx/store';
 | 
			
		||||
import { selectIsAuthenticated } from '@core/auth/auth.selectors';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
import { tap } from 'rxjs/operators';
 | 
			
		||||
import { map, tap } from 'rxjs/operators';
 | 
			
		||||
 | 
			
		||||
declare const System;
 | 
			
		||||
 | 
			
		||||
@ -106,6 +106,37 @@ export class ResourcesService {
 | 
			
		||||
    return this.loadResourceByType(fileType, url);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public downloadResource(downloadUrl: string): Observable<any> {
 | 
			
		||||
    return this.http.get(downloadUrl, {
 | 
			
		||||
      responseType: 'arraybuffer',
 | 
			
		||||
      observe: 'response'
 | 
			
		||||
    }).pipe(
 | 
			
		||||
      map((response) => {
 | 
			
		||||
        const headers = response.headers;
 | 
			
		||||
        const filename = headers.get('x-filename');
 | 
			
		||||
        const contentType = headers.get('content-type');
 | 
			
		||||
        const linkElement = document.createElement('a');
 | 
			
		||||
        try {
 | 
			
		||||
          const blob = new Blob([response.body], {type: contentType});
 | 
			
		||||
          const url = URL.createObjectURL(blob);
 | 
			
		||||
          linkElement.setAttribute('href', url);
 | 
			
		||||
          linkElement.setAttribute('download', filename);
 | 
			
		||||
          const clickEvent = new MouseEvent('click',
 | 
			
		||||
            {
 | 
			
		||||
              view: window,
 | 
			
		||||
              bubbles: true,
 | 
			
		||||
              cancelable: false
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
          linkElement.dispatchEvent(clickEvent);
 | 
			
		||||
          return null;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          throw e;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public loadFactories(resourceId: string | TbResourceId, modulesMap: IModulesMap): Observable<ModulesWithFactories> {
 | 
			
		||||
    const url = this.getDownloadUrl(resourceId);
 | 
			
		||||
    if (this.loadedModulesAndFactories[url]) {
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,10 @@
 | 
			
		||||
    <div class="tb-form-panel-title" translate>gateway.download-configuration-file</div>
 | 
			
		||||
    <div class="tb-form-row no-border no-padding space-between">
 | 
			
		||||
      <div class="tb-no-data-text tb-commands-hint" translate>gateway.download-docker-compose</div>
 | 
			
		||||
      <a mat-stroked-button color="primary" href="{{downloadUrl}}" target="_blank">
 | 
			
		||||
      <button mat-stroked-button color="primary" (click)="download($event)">
 | 
			
		||||
        <mat-icon>download</mat-icon>
 | 
			
		||||
        {{ 'action.download' | translate }}
 | 
			
		||||
      </a>
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,8 +14,8 @@
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import { Component, Inject, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { WINDOW } from '@core/services/window.service';
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
import { DeviceService } from '@core/http/device.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-gateway-command',
 | 
			
		||||
@ -23,20 +23,20 @@ import { WINDOW } from '@core/services/window.service';
 | 
			
		||||
  styleUrls: ['./device-gateway-command.component.scss']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export class DeviceGatewayCommandComponent implements OnInit {
 | 
			
		||||
export class DeviceGatewayCommandComponent {
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  deviceId: string;
 | 
			
		||||
 | 
			
		||||
  downloadUrl: string;
 | 
			
		||||
 | 
			
		||||
  constructor(@Inject(WINDOW) private window: Window) {
 | 
			
		||||
  constructor(private deviceService: DeviceService) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  download($event: Event) {
 | 
			
		||||
    if ($event) {
 | 
			
		||||
      $event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
    if (this.deviceId) {
 | 
			
		||||
      this.downloadUrl = `${this.window.location.origin}/api/device-connectivity/gateway-launch/${this.deviceId}/docker-compose/download`;
 | 
			
		||||
      this.deviceService.downloadGatewayDockerComposeFile(this.deviceId).subscribe(() => {});
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -865,14 +865,6 @@ export interface PublishTelemetryCommand {
 | 
			
		||||
  snmp?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface PublishLaunchCommand {
 | 
			
		||||
  mqtt: {
 | 
			
		||||
    linux: string;
 | 
			
		||||
    windows: string;
 | 
			
		||||
    macos: string;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const dayOfWeekTranslations = new Array<string>(
 | 
			
		||||
  'device-profile.schedule-day.monday',
 | 
			
		||||
  'device-profile.schedule-day.tuesday',
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user