mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Add UI telemetry (#6981)
* Load Segment module * First events * Add GET /telemetry epoint to API * Init telemetry with data from API * Add more tracking * Update ui static files * Send telemetry for tab changes * Update UI static files * Set IP to 0.0.0.0 * Update UI static files
This commit is contained in:
3211
ui/package-lock.json
generated
3211
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@
|
||||
"@angular/platform-browser-dynamic": "^15.2.0",
|
||||
"@angular/router": "^15.2.0",
|
||||
"mermaid": "^10.0.2",
|
||||
"ngx-segment-analytics": "^16.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
|
||||
1
ui/src/app/api-gen/.openapi-generator/FILES
generated
1
ui/src/app/api-gen/.openapi-generator/FILES
generated
@@ -39,5 +39,6 @@ model/metadata.ts
|
||||
model/metadataRequest.ts
|
||||
model/models.ts
|
||||
model/resource.ts
|
||||
model/telemetryResponse.ts
|
||||
param.ts
|
||||
variables.ts
|
||||
|
||||
56
ui/src/app/api-gen/api/default.service.ts
generated
56
ui/src/app/api-gen/api/default.service.ts
generated
@@ -60,6 +60,8 @@ import { GeneralSuccess } from '../model/generalSuccess';
|
||||
import { InstanceGet200Response } from '../model/instanceGet200Response';
|
||||
// @ts-ignore
|
||||
import { MetadataRequest } from '../model/metadataRequest';
|
||||
// @ts-ignore
|
||||
import { TelemetryResponse } from '../model/telemetryResponse';
|
||||
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
|
||||
@@ -1717,4 +1719,58 @@ export class DefaultService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specific information for sending telemetry data
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
*/
|
||||
public telemetryGet(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<TelemetryResponse>;
|
||||
public telemetryGet(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<TelemetryResponse>>;
|
||||
public telemetryGet(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<TelemetryResponse>>;
|
||||
public telemetryGet(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||
|
||||
let localVarHeaders = this.defaultHeaders;
|
||||
|
||||
let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
|
||||
if (localVarHttpHeaderAcceptSelected === undefined) {
|
||||
// to determine the Accept header
|
||||
const httpHeaderAccepts: string[] = [
|
||||
'application/json'
|
||||
];
|
||||
localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||
}
|
||||
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||
}
|
||||
|
||||
let localVarHttpContext: HttpContext | undefined = options && options.context;
|
||||
if (localVarHttpContext === undefined) {
|
||||
localVarHttpContext = new HttpContext();
|
||||
}
|
||||
|
||||
|
||||
let responseType_: 'text' | 'json' | 'blob' = 'json';
|
||||
if (localVarHttpHeaderAcceptSelected) {
|
||||
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||
responseType_ = 'text';
|
||||
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
|
||||
responseType_ = 'json';
|
||||
} else {
|
||||
responseType_ = 'blob';
|
||||
}
|
||||
}
|
||||
|
||||
let localVarPath = `/telemetry`;
|
||||
return this.httpClient.request<TelemetryResponse>('get', `${this.configuration.basePath}${localVarPath}`,
|
||||
{
|
||||
context: localVarHttpContext,
|
||||
responseType: <any>responseType_,
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: localVarHeaders,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
ui/src/app/api-gen/model/models.ts
generated
1
ui/src/app/api-gen/model/models.ts
generated
@@ -29,3 +29,4 @@ export * from './instanceGet200Response';
|
||||
export * from './metadata';
|
||||
export * from './metadataRequest';
|
||||
export * from './resource';
|
||||
export * from './telemetryResponse';
|
||||
|
||||
19
ui/src/app/api-gen/model/telemetryGet200Response.ts
generated
Normal file
19
ui/src/app/api-gen/model/telemetryGet200Response.ts
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* odo dev
|
||||
* API interface for \'odo dev\'
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface TelemetryGet200Response {
|
||||
enabled: boolean;
|
||||
apikey?: string;
|
||||
userid?: string;
|
||||
}
|
||||
|
||||
19
ui/src/app/api-gen/model/telemetryResponse.ts
generated
Normal file
19
ui/src/app/api-gen/model/telemetryResponse.ts
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* odo dev
|
||||
* API interface for \'odo dev\'
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface TelemetryResponse {
|
||||
enabled: boolean;
|
||||
apikey?: string;
|
||||
userid?: string;
|
||||
}
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
<div class="flex-container">
|
||||
|
||||
<div class="flex-child">
|
||||
<mat-tab-group animationDuration="0">
|
||||
<mat-tab-group animationDuration="0" (selectedTabChange)="onSelectedTabChange($event)">
|
||||
|
||||
<mat-tab data-cy="tab-yaml" label="YAML">
|
||||
<mat-tab data-cy="tab-yaml" label="{{tabNames[0]}}">
|
||||
<div class="tab-content">
|
||||
<div *ngIf="errorMessage" data-cy="yaml-error" class="error-message">{{errorMessage}}</div>
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>Devfile YAML</mat-label>
|
||||
<textarea data-cy="yaml-input" matInput #input id="input" rows="20" [value]="devfileYaml"></textarea>
|
||||
</mat-form-field>
|
||||
<button data-cy="yaml-send" matTooltip="Save Devfile to disk" mat-flat-button color="primary" (click)="onButtonClick(input.value, true)">Save</button>
|
||||
<button data-cy="yaml-save" matTooltip="Apply changes to other tabs" mat-flat-button color="normal" (click)="onButtonClick(input.value, false)">Apply</button>
|
||||
<button data-cy="yaml-send" matTooltip="Save Devfile to disk" mat-flat-button color="primary" (click)="onSave(input.value)">Save</button>
|
||||
<button data-cy="yaml-save" matTooltip="Apply changes to other tabs" mat-flat-button color="normal" (click)="onApply(input.value)">Apply</button>
|
||||
<button data-cy="yaml-clear" matTooltip="Clear Devfile content" mat-flat-button color="normal" (click)="clear()">Clear</button>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab data-cy="tab-chart" label="Chart">
|
||||
<mat-tab data-cy="tab-chart" label="{{tabNames[1]}}">
|
||||
<div class="flex-child">
|
||||
<div #mermaid id="mermaid" class="mermaid" [innerHTML]="sanitizer.bypassSecurityTrustHtml(mermaidContent)"></div>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<mat-tab data-cy="tab-metadata">
|
||||
<ng-template mat-tab-label>
|
||||
Metadata
|
||||
{{tabNames[2]}}
|
||||
</ng-template>
|
||||
<app-metadata></app-metadata>
|
||||
</mat-tab>
|
||||
@@ -40,7 +40,7 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">code</mat-icon>
|
||||
Commands
|
||||
{{tabNames[3]}}
|
||||
</ng-template>
|
||||
<app-commands></app-commands>
|
||||
</mat-tab>
|
||||
@@ -48,7 +48,7 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">alarm</mat-icon>
|
||||
Events
|
||||
{{tabNames[4]}}
|
||||
</ng-template>
|
||||
<app-events></app-events>
|
||||
</mat-tab>
|
||||
@@ -56,7 +56,7 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">width_normal</mat-icon>
|
||||
Containers
|
||||
{{tabNames[5]}}
|
||||
</ng-template>
|
||||
<app-containers></app-containers>
|
||||
</mat-tab>
|
||||
@@ -64,7 +64,7 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">image</mat-icon>
|
||||
Images
|
||||
{{tabNames[6]}}
|
||||
</ng-template>
|
||||
<app-images></app-images>
|
||||
</mat-tab>
|
||||
@@ -72,7 +72,7 @@
|
||||
<mat-tab >
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">description</mat-icon>
|
||||
Resources
|
||||
{{tabNames[7]}}
|
||||
</ng-template>
|
||||
<app-resources></app-resources>
|
||||
</mat-tab>
|
||||
@@ -80,7 +80,7 @@
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon material-icons-outlined">storage</mat-icon>
|
||||
Volumes
|
||||
{{tabNames[8]}}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ import { MatIconRegistry } from "@angular/material/icon";
|
||||
import { OdoapiService } from './services/odoapi.service';
|
||||
import { SseService } from './services/sse.service';
|
||||
import {DevfileContent} from "./api-gen";
|
||||
import { TelemetryResponse } from './api-gen';
|
||||
import { MatTabChangeEvent } from '@angular/material/tabs';
|
||||
import { TelemetryService } from './services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -15,6 +18,17 @@ import {DevfileContent} from "./api-gen";
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
protected tabNames: string[] = [
|
||||
"YAML",
|
||||
"Chart",
|
||||
"Metadata",
|
||||
"Commands",
|
||||
"Events",
|
||||
"Containers",
|
||||
"Images",
|
||||
"Resources",
|
||||
"Volumes"
|
||||
];
|
||||
protected mermaidContent: string = "";
|
||||
protected devfileYaml: string = "";
|
||||
protected errorMessage: string = "";
|
||||
@@ -27,6 +41,7 @@ export class AppComponent implements OnInit {
|
||||
private mermaid: MermaidService,
|
||||
private state: StateService,
|
||||
private sse: SseService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.matIconRegistry.addSvgIcon(
|
||||
`github`,
|
||||
@@ -44,7 +59,7 @@ export class AppComponent implements OnInit {
|
||||
devfile.subscribe({
|
||||
next: (devfile) => {
|
||||
if (devfile.content != undefined) {
|
||||
this.onButtonClick(devfile.content, false);
|
||||
this.propagateChange(devfile.content, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -71,18 +86,31 @@ export class AppComponent implements OnInit {
|
||||
this.sse.subscribeTo(['DevfileUpdated']).subscribe(event => {
|
||||
let newDevfile: DevfileContent = JSON.parse(event.data)
|
||||
if (newDevfile.content != undefined) {
|
||||
this.onButtonClick(newDevfile.content, false);
|
||||
this.propagateChange(newDevfile.content, false);
|
||||
}
|
||||
});
|
||||
|
||||
this.odoApi.telemetry().subscribe({
|
||||
next: (data: TelemetryResponse) => {
|
||||
if (data.enabled) {
|
||||
if (data.apikey == null || data.userid == null) {
|
||||
return;
|
||||
}
|
||||
this.telemetry.init(data.apikey, data.userid)
|
||||
this.telemetry.track("[ui] start");
|
||||
}
|
||||
},
|
||||
error: () => {}
|
||||
})
|
||||
}
|
||||
|
||||
onButtonClick(content: string, save: boolean){
|
||||
propagateChange(content: string, saveToApi: boolean){
|
||||
const result = this.wasmGo.setDevfileContent(content);
|
||||
result.subscribe({
|
||||
next: (value) => {
|
||||
this.errorMessage = '';
|
||||
this.state.changeDevfileYaml(value);
|
||||
if (save) {
|
||||
if (saveToApi) {
|
||||
this.odoApi.saveDevfile(value.content).subscribe({
|
||||
next: () => {},
|
||||
error: (error) => {
|
||||
@@ -97,13 +125,28 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
onSave(content: string) {
|
||||
this.telemetry.track("[ui] save devfile to disk");
|
||||
this.propagateChange(content, true);
|
||||
}
|
||||
|
||||
onApply(content: string) {
|
||||
this.telemetry.track("[ui] change devfile from textarea");
|
||||
this.propagateChange(content, false);
|
||||
}
|
||||
|
||||
clear() {
|
||||
if (confirm('You will delete the content of the Devfile. Continue?')) {
|
||||
this.telemetry.track("[ui] clear devfile");
|
||||
this.wasmGo.clearDevfileContent().subscribe({
|
||||
next: (value) => {
|
||||
this.onButtonClick(value.content, false);
|
||||
this.propagateChange(value.content, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onSelectedTabChange(e: MatTabChangeEvent) {
|
||||
this.telemetry.track("[ui] change to tab "+this.tabNames[e.index]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
import { SegmentModule } from 'ngx-segment-analytics';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { MetadataComponent } from './forms/metadata/metadata.component';
|
||||
import { MultiTextComponent } from './controls/multi-text/multi-text.component';
|
||||
@@ -86,7 +88,9 @@ import { ChipsEventsComponent } from './controls/chips-events/chips-events.compo
|
||||
MatSelectModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule
|
||||
MatTooltipModule,
|
||||
|
||||
SegmentModule.forRoot({ loadOnInitialization: false })
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
@@ -4,6 +4,7 @@ import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { PATTERN_COMMAND_ID } from '../patterns';
|
||||
import { Resource } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-command-apply',
|
||||
@@ -21,6 +22,7 @@ export class CommandApplyComponent {
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private state: StateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMMAND_ID)]),
|
||||
@@ -37,6 +39,7 @@ export class CommandApplyComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.telemetry.track("[ui] create apply command");
|
||||
const subcreate = () => {
|
||||
const result = this.devstate.addApplyCommand(this.form.value["name"], this.form.value);
|
||||
result.subscribe({
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { PATTERN_COMMAND_ID } from '../patterns';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-command-composite',
|
||||
@@ -18,6 +19,7 @@ export class CommandCompositeComponent {
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private state: StateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMMAND_ID)]),
|
||||
@@ -36,6 +38,7 @@ export class CommandCompositeComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.telemetry.track("[ui] create composite command");
|
||||
const result = this.devstate.addCompositeCommand(this.form.value["name"], this.form.value);
|
||||
result.subscribe({
|
||||
next: (value) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { PATTERN_COMMAND_ID } from '../patterns';
|
||||
import { Container } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-command-exec',
|
||||
@@ -21,6 +22,7 @@ export class CommandExecComponent {
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private state: StateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMMAND_ID)]),
|
||||
@@ -41,7 +43,7 @@ export class CommandExecComponent {
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
this.telemetry.track("[ui] create exec command");
|
||||
const subcreate = () => {
|
||||
const result = this.devstate.addExecCommand(this.form.value["name"], this.form.value);
|
||||
result.subscribe({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { PATTERN_COMMAND_ID } from '../patterns';
|
||||
import { Image } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-command-image',
|
||||
@@ -21,6 +22,7 @@ export class CommandImageComponent {
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private state: StateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMMAND_ID)]),
|
||||
@@ -37,7 +39,7 @@ export class CommandImageComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
this.telemetry.track("[ui] create image command");
|
||||
const subcreate = () => {
|
||||
const result = this.devstate.addApplyCommand(this.form.value["name"], this.form.value);
|
||||
result.subscribe({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { PATTERN_COMPONENT_ID } from '../patterns';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { Observable, of, map, catchError } from 'rxjs';
|
||||
import { Container } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-container',
|
||||
@@ -22,6 +23,7 @@ export class ContainerComponent {
|
||||
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMPONENT_ID)]),
|
||||
@@ -36,6 +38,7 @@ export class ContainerComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.telemetry.track("[ui] create container");
|
||||
this.created.emit(this.form.value);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { PATTERN_COMPONENT_ID } from '../patterns';
|
||||
import { Image } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-image',
|
||||
@@ -15,7 +16,9 @@ export class ImageComponent {
|
||||
|
||||
form: FormGroup;
|
||||
|
||||
constructor() {
|
||||
constructor(
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMPONENT_ID)]),
|
||||
imageName: new FormControl("", [Validators.required]),
|
||||
@@ -27,6 +30,7 @@ export class ImageComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.telemetry.track("[ui] create image");
|
||||
this.created.emit(this.form.value);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
const semverPattern = `^([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\-[0-9a-z-]+(\\.[0-9a-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$`;
|
||||
|
||||
@@ -17,6 +18,7 @@ export class MetadataComponent implements OnInit {
|
||||
constructor(
|
||||
private devstate: DevstateService,
|
||||
private state: StateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl(''),
|
||||
@@ -46,6 +48,7 @@ export class MetadataComponent implements OnInit {
|
||||
}
|
||||
|
||||
onSave() {
|
||||
this.telemetry.track("[ui] apply metadata");
|
||||
const result = this.devstate.setMetadata(this.form.value);
|
||||
result.subscribe({
|
||||
next: (value) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { PATTERN_COMPONENT_ID } from '../patterns';
|
||||
import { Resource } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-resource',
|
||||
@@ -16,7 +17,9 @@ export class ResourceComponent {
|
||||
form: FormGroup;
|
||||
uriOrInlined: string = 'uri';
|
||||
|
||||
constructor() {
|
||||
constructor(
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl("", [Validators.required, Validators.pattern(PATTERN_COMPONENT_ID)]),
|
||||
uri: new FormControl("", [Validators.required]),
|
||||
@@ -42,6 +45,7 @@ export class ResourceComponent {
|
||||
}
|
||||
|
||||
create() {
|
||||
this.telemetry.track("[ui] create resource");
|
||||
this.created.emit(this.form.value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DevfileGet200Response, GeneralSuccess } from '../api-gen';
|
||||
import { DevfileGet200Response, GeneralSuccess, TelemetryResponse } from '../api-gen';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -21,4 +21,8 @@ export class OdoapiService {
|
||||
content: content
|
||||
});
|
||||
}
|
||||
|
||||
telemetry(): Observable<TelemetryResponse> {
|
||||
return this.http.get<TelemetryResponse>(this.base+"/telemetry");
|
||||
}
|
||||
}
|
||||
|
||||
16
ui/src/app/services/telemetry.service.spec.ts
Normal file
16
ui/src/app/services/telemetry.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TelemetryService } from './telemetry.service';
|
||||
|
||||
describe('TelemetryService', () => {
|
||||
let service: TelemetryService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(TelemetryService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
28
ui/src/app/services/telemetry.service.ts
Normal file
28
ui/src/app/services/telemetry.service.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { SegmentService } from 'ngx-segment-analytics';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TelemetryService {
|
||||
|
||||
private options = {
|
||||
context: {
|
||||
ip: "0.0.0.0"
|
||||
}
|
||||
};
|
||||
|
||||
constructor(
|
||||
private segment: SegmentService
|
||||
) { }
|
||||
|
||||
init(apikey: string, userid: string) {
|
||||
this.segment.identify(userid, {}, this.options);
|
||||
this.segment.load(apikey);
|
||||
this.segment.setAnonymousId(userid);
|
||||
}
|
||||
|
||||
track(event: string) {
|
||||
this.segment.track(event, {}, this.options);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { Component } from '@angular/core';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { Command } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-commands',
|
||||
@@ -21,6 +22,7 @@ export class CommandsComponent {
|
||||
constructor(
|
||||
private state: StateService,
|
||||
private devstate: DevstateService,
|
||||
private telemetry: TelemetryService
|
||||
) {
|
||||
this.enableDragAndDrop = this.state.getDragAndDropEnabled();
|
||||
}
|
||||
@@ -39,6 +41,7 @@ export class CommandsComponent {
|
||||
}
|
||||
|
||||
displayExecForm() {
|
||||
this.telemetry.track("[ui] start create exec command");
|
||||
this.forceDisplayExecForm = true;
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
@@ -46,6 +49,7 @@ export class CommandsComponent {
|
||||
}
|
||||
|
||||
displayApplyForm() {
|
||||
this.telemetry.track("[ui] start create apply command");
|
||||
this.forceDisplayApplyForm = true;
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
@@ -53,6 +57,7 @@ export class CommandsComponent {
|
||||
}
|
||||
|
||||
displayImageForm() {
|
||||
this.telemetry.track("[ui] start create image command");
|
||||
this.forceDisplayImageForm = true;
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
@@ -60,6 +65,7 @@ export class CommandsComponent {
|
||||
}
|
||||
|
||||
displayCompositeForm() {
|
||||
this.telemetry.track("[ui] start create composite command");
|
||||
this.forceDisplayCompositeForm = true;
|
||||
setTimeout(() => {
|
||||
this.scrollToBottom();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component } from '@angular/core';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { DevstateService } from 'src/app/services/devstate.service';
|
||||
import { Events } from 'src/app/api-gen';
|
||||
import { TelemetryService } from 'src/app/services/telemetry.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-events',
|
||||
@@ -16,6 +17,7 @@ export class EventsComponent {
|
||||
constructor(
|
||||
private state: StateService,
|
||||
private devstate: DevstateService,
|
||||
private telemetry: TelemetryService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -26,6 +28,7 @@ export class EventsComponent {
|
||||
}
|
||||
|
||||
onUpdate(event: "preStart" | "postStart" | "preStop" | "postStop", commands: string[]) {
|
||||
this.telemetry.track("[ui] add "+event+" event");
|
||||
const result = this.devstate.updateEvents(event, commands);
|
||||
result.subscribe({
|
||||
next: (value) => {
|
||||
|
||||
Reference in New Issue
Block a user