diff --git a/src/api/api/authService.service.ts b/src/api/api/authService.service.ts index 15edf104..8b8d383d 100644 --- a/src/api/api/authService.service.ts +++ b/src/api/api/authService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/configService.service.ts b/src/api/api/configService.service.ts index 6d604c25..6c1fa384 100644 --- a/src/api/api/configService.service.ts +++ b/src/api/api/configService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/cronWorkflowService.service.ts b/src/api/api/cronWorkflowService.service.ts index 58ad8ad4..2a62cd63 100644 --- a/src/api/api/cronWorkflowService.service.ts +++ b/src/api/api/cronWorkflowService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/labelService.service.ts b/src/api/api/labelService.service.ts index 9868f855..d17370b6 100644 --- a/src/api/api/labelService.service.ts +++ b/src/api/api/labelService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/namespaceService.service.ts b/src/api/api/namespaceService.service.ts index c92c847c..360905bb 100644 --- a/src/api/api/namespaceService.service.ts +++ b/src/api/api/namespaceService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/secretService.service.ts b/src/api/api/secretService.service.ts index 05047e64..4f30cc68 100644 --- a/src/api/api/secretService.service.ts +++ b/src/api/api/secretService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/serviceService.service.ts b/src/api/api/serviceService.service.ts index b87e649b..b40416a2 100644 --- a/src/api/api/serviceService.service.ts +++ b/src/api/api/serviceService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/workflowService.service.ts b/src/api/api/workflowService.service.ts index f6ce9b5d..eab59dd0 100644 --- a/src/api/api/workflowService.service.ts +++ b/src/api/api/workflowService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/workflowTemplateService.service.ts b/src/api/api/workflowTemplateService.service.ts index 7521f69d..33d4dce7 100644 --- a/src/api/api/workflowTemplateService.service.ts +++ b/src/api/api/workflowTemplateService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/api/workspaceService.service.ts b/src/api/api/workspaceService.service.ts index 2e43bc6d..a0343e88 100644 --- a/src/api/api/workspaceService.service.ts +++ b/src/api/api/workspaceService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -21,6 +21,7 @@ import { CreateWorkspaceBody } from '../model/models'; import { GetWorkspaceStatisticsForNamespaceResponse } from '../model/models'; import { GoogleRpcStatus } from '../model/models'; import { ListWorkspaceResponse } from '../model/models'; +import { StreamResultOfLogStreamResponse } from '../model/models'; import { UpdateWorkspaceBody } from '../model/models'; import { Workspace } from '../model/models'; import { WorkspaceStatus } from '../model/models'; @@ -272,6 +273,67 @@ export class WorkspaceServiceService { ); } + /** + * @param namespace + * @param uid + * @param containerName + * @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 getWorkspaceContainerLogs(namespace: string, uid: string, containerName: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/octet-stream'}): Observable; + public getWorkspaceContainerLogs(namespace: string, uid: string, containerName: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/octet-stream'}): Observable>; + public getWorkspaceContainerLogs(namespace: string, uid: string, containerName: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/octet-stream'}): Observable>; + public getWorkspaceContainerLogs(namespace: string, uid: string, containerName: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/octet-stream'}): Observable { + if (namespace === null || namespace === undefined) { + throw new Error('Required parameter namespace was null or undefined when calling getWorkspaceContainerLogs.'); + } + if (uid === null || uid === undefined) { + throw new Error('Required parameter uid was null or undefined when calling getWorkspaceContainerLogs.'); + } + if (containerName === null || containerName === undefined) { + throw new Error('Required parameter containerName was null or undefined when calling getWorkspaceContainerLogs.'); + } + + let headers = this.defaultHeaders; + + // authentication (Bearer) required + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["Bearer"] || this.configuration.apiKeys["authorization"]; + if (key) { + headers = headers.set('authorization', key); + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json', + 'application/octet-stream' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/apis/v1beta1/${encodeURIComponent(String(namespace))}/workspaces/${encodeURIComponent(String(uid))}/containers/${encodeURIComponent(String(containerName))}/logs`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + /** * @param namespace * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. diff --git a/src/api/api/workspaceTemplateService.service.ts b/src/api/api/workspaceTemplateService.service.ts index c0067bec..0ac2681f 100644 --- a/src/api/api/workspaceTemplateService.service.ts +++ b/src/api/api/workspaceTemplateService.service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/addSecretKeyValueResponse.ts b/src/api/model/addSecretKeyValueResponse.ts index 9be3cba4..38e91f8f 100644 --- a/src/api/model/addSecretKeyValueResponse.ts +++ b/src/api/model/addSecretKeyValueResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/addWorkflowExecutionsMetricsRequest.ts b/src/api/model/addWorkflowExecutionsMetricsRequest.ts index adb7a9e0..5b9557b6 100644 --- a/src/api/model/addWorkflowExecutionsMetricsRequest.ts +++ b/src/api/model/addWorkflowExecutionsMetricsRequest.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/archiveWorkflowTemplateResponse.ts b/src/api/model/archiveWorkflowTemplateResponse.ts index d8181601..91020f4a 100644 --- a/src/api/model/archiveWorkflowTemplateResponse.ts +++ b/src/api/model/archiveWorkflowTemplateResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/artifactResponse.ts b/src/api/model/artifactResponse.ts index e67b9352..067a74c5 100644 --- a/src/api/model/artifactResponse.ts +++ b/src/api/model/artifactResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/createWorkflowExecutionBody.ts b/src/api/model/createWorkflowExecutionBody.ts index 480604ac..4e106d13 100644 --- a/src/api/model/createWorkflowExecutionBody.ts +++ b/src/api/model/createWorkflowExecutionBody.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/createWorkspaceBody.ts b/src/api/model/createWorkspaceBody.ts index 541943d1..6b5523ec 100644 --- a/src/api/model/createWorkspaceBody.ts +++ b/src/api/model/createWorkspaceBody.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/cronWorkflow.ts b/src/api/model/cronWorkflow.ts index 79e75abe..30bc4ec7 100644 --- a/src/api/model/cronWorkflow.ts +++ b/src/api/model/cronWorkflow.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/cronWorkflowStatisticsReport.ts b/src/api/model/cronWorkflowStatisticsReport.ts index 683dd898..7439b0f6 100644 --- a/src/api/model/cronWorkflowStatisticsReport.ts +++ b/src/api/model/cronWorkflowStatisticsReport.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/deleteSecretKeyResponse.ts b/src/api/model/deleteSecretKeyResponse.ts index fe4330eb..84057d24 100644 --- a/src/api/model/deleteSecretKeyResponse.ts +++ b/src/api/model/deleteSecretKeyResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/deleteSecretResponse.ts b/src/api/model/deleteSecretResponse.ts index 5a86c6d9..a7246712 100644 --- a/src/api/model/deleteSecretResponse.ts +++ b/src/api/model/deleteSecretResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/file.ts b/src/api/model/file.ts index 5bf896ab..fe0d1188 100644 --- a/src/api/model/file.ts +++ b/src/api/model/file.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getAccessTokenRequest.ts b/src/api/model/getAccessTokenRequest.ts index e551a84c..698cc3c9 100644 --- a/src/api/model/getAccessTokenRequest.ts +++ b/src/api/model/getAccessTokenRequest.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getAccessTokenResponse.ts b/src/api/model/getAccessTokenResponse.ts index 55b703a5..0f28b43e 100644 --- a/src/api/model/getAccessTokenResponse.ts +++ b/src/api/model/getAccessTokenResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getConfigResponse.ts b/src/api/model/getConfigResponse.ts index 0fe78f44..71664c3c 100644 --- a/src/api/model/getConfigResponse.ts +++ b/src/api/model/getConfigResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getLabelsResponse.ts b/src/api/model/getLabelsResponse.ts index 5059b87c..405bc1c4 100644 --- a/src/api/model/getLabelsResponse.ts +++ b/src/api/model/getLabelsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getWorkflowExecutionMetricsResponse.ts b/src/api/model/getWorkflowExecutionMetricsResponse.ts index 157301b4..3a966f22 100644 --- a/src/api/model/getWorkflowExecutionMetricsResponse.ts +++ b/src/api/model/getWorkflowExecutionMetricsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getWorkflowExecutionStatisticsForNamespaceResponse.ts b/src/api/model/getWorkflowExecutionStatisticsForNamespaceResponse.ts index 52bc4058..5b56d834 100644 --- a/src/api/model/getWorkflowExecutionStatisticsForNamespaceResponse.ts +++ b/src/api/model/getWorkflowExecutionStatisticsForNamespaceResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/getWorkspaceStatisticsForNamespaceResponse.ts b/src/api/model/getWorkspaceStatisticsForNamespaceResponse.ts index 2cef6a46..9de5d465 100644 --- a/src/api/model/getWorkspaceStatisticsForNamespaceResponse.ts +++ b/src/api/model/getWorkspaceStatisticsForNamespaceResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/googleProtobufAny.ts b/src/api/model/googleProtobufAny.ts index 6691d5f4..8c6ca40c 100644 --- a/src/api/model/googleProtobufAny.ts +++ b/src/api/model/googleProtobufAny.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/googleRpcStatus.ts b/src/api/model/googleRpcStatus.ts index 52eaa88e..c54d924d 100644 --- a/src/api/model/googleRpcStatus.ts +++ b/src/api/model/googleRpcStatus.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/isAuthorized.ts b/src/api/model/isAuthorized.ts index 2e4ecff1..539d4a97 100644 --- a/src/api/model/isAuthorized.ts +++ b/src/api/model/isAuthorized.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/isAuthorizedResponse.ts b/src/api/model/isAuthorizedResponse.ts index 1c530058..14f4313e 100644 --- a/src/api/model/isAuthorizedResponse.ts +++ b/src/api/model/isAuthorizedResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/isValidTokenRequest.ts b/src/api/model/isValidTokenRequest.ts index afbe9825..908ae4d5 100644 --- a/src/api/model/isValidTokenRequest.ts +++ b/src/api/model/isValidTokenRequest.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/isValidTokenResponse.ts b/src/api/model/isValidTokenResponse.ts index 2e96d706..8e32b51c 100644 --- a/src/api/model/isValidTokenResponse.ts +++ b/src/api/model/isValidTokenResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/keyValue.ts b/src/api/model/keyValue.ts index 66db17e4..f7e29d2d 100644 --- a/src/api/model/keyValue.ts +++ b/src/api/model/keyValue.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/labels.ts b/src/api/model/labels.ts index 30b131da..3e539d2c 100644 --- a/src/api/model/labels.ts +++ b/src/api/model/labels.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listCronWorkflowsResponse.ts b/src/api/model/listCronWorkflowsResponse.ts index 7af40e2a..6dcc02f8 100644 --- a/src/api/model/listCronWorkflowsResponse.ts +++ b/src/api/model/listCronWorkflowsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listFilesResponse.ts b/src/api/model/listFilesResponse.ts index 3af02c73..499319fe 100644 --- a/src/api/model/listFilesResponse.ts +++ b/src/api/model/listFilesResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listNamespacesResponse.ts b/src/api/model/listNamespacesResponse.ts index 8981e9a9..bd074d40 100644 --- a/src/api/model/listNamespacesResponse.ts +++ b/src/api/model/listNamespacesResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listSecretsResponse.ts b/src/api/model/listSecretsResponse.ts index d7666fe2..9cb416c6 100644 --- a/src/api/model/listSecretsResponse.ts +++ b/src/api/model/listSecretsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listServicesResponse.ts b/src/api/model/listServicesResponse.ts index 66f35116..9e7e0e3f 100644 --- a/src/api/model/listServicesResponse.ts +++ b/src/api/model/listServicesResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkflowExecutionsResponse.ts b/src/api/model/listWorkflowExecutionsResponse.ts index 1b453842..59f87f1a 100644 --- a/src/api/model/listWorkflowExecutionsResponse.ts +++ b/src/api/model/listWorkflowExecutionsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkflowTemplateVersionsResponse.ts b/src/api/model/listWorkflowTemplateVersionsResponse.ts index d80df8f4..ba818859 100644 --- a/src/api/model/listWorkflowTemplateVersionsResponse.ts +++ b/src/api/model/listWorkflowTemplateVersionsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkflowTemplatesResponse.ts b/src/api/model/listWorkflowTemplatesResponse.ts index 0c2d2173..eb0ee436 100644 --- a/src/api/model/listWorkflowTemplatesResponse.ts +++ b/src/api/model/listWorkflowTemplatesResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkspaceResponse.ts b/src/api/model/listWorkspaceResponse.ts index 0407eea8..91354e62 100644 --- a/src/api/model/listWorkspaceResponse.ts +++ b/src/api/model/listWorkspaceResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkspaceTemplateVersionsResponse.ts b/src/api/model/listWorkspaceTemplateVersionsResponse.ts index 1b2fcd60..3d870df5 100644 --- a/src/api/model/listWorkspaceTemplateVersionsResponse.ts +++ b/src/api/model/listWorkspaceTemplateVersionsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/listWorkspaceTemplatesResponse.ts b/src/api/model/listWorkspaceTemplatesResponse.ts index 97ba9f47..8a44df91 100644 --- a/src/api/model/listWorkspaceTemplatesResponse.ts +++ b/src/api/model/listWorkspaceTemplatesResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/logEntry.ts b/src/api/model/logEntry.ts index a734ed86..9a4a2782 100644 --- a/src/api/model/logEntry.ts +++ b/src/api/model/logEntry.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/logStreamResponse.ts b/src/api/model/logStreamResponse.ts index 28f01975..3ef05dc0 100644 --- a/src/api/model/logStreamResponse.ts +++ b/src/api/model/logStreamResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/metric.ts b/src/api/model/metric.ts index 47bd4a84..4c01f522 100644 --- a/src/api/model/metric.ts +++ b/src/api/model/metric.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/models.ts b/src/api/model/models.ts index 40cf8a66..aa9dbb1e 100644 --- a/src/api/model/models.ts +++ b/src/api/model/models.ts @@ -59,6 +59,7 @@ export * from './workflowExecutionStatus'; export * from './workflowExecutionsMetricsResponse'; export * from './workflowTemplate'; export * from './workspace'; +export * from './workspaceComponent'; export * from './workspaceStatisticReport'; export * from './workspaceStatus'; export * from './workspaceTemplate'; diff --git a/src/api/model/namespace.ts b/src/api/model/namespace.ts index 796f8eeb..14978e2e 100644 --- a/src/api/model/namespace.ts +++ b/src/api/model/namespace.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/nodePool.ts b/src/api/model/nodePool.ts index 1380920e..ba7ed034 100644 --- a/src/api/model/nodePool.ts +++ b/src/api/model/nodePool.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/nodePoolOption.ts b/src/api/model/nodePoolOption.ts index c1a53bdf..6ee84621 100644 --- a/src/api/model/nodePoolOption.ts +++ b/src/api/model/nodePoolOption.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/parameter.ts b/src/api/model/parameter.ts index fd11763b..ad28b895 100644 --- a/src/api/model/parameter.ts +++ b/src/api/model/parameter.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/parameterOption.ts b/src/api/model/parameterOption.ts index 78b4f8fb..56153c2c 100644 --- a/src/api/model/parameterOption.ts +++ b/src/api/model/parameterOption.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/secret.ts b/src/api/model/secret.ts index e7989c09..db9eed1a 100644 --- a/src/api/model/secret.ts +++ b/src/api/model/secret.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/secretExistsResponse.ts b/src/api/model/secretExistsResponse.ts index b267b44c..bebd4789 100644 --- a/src/api/model/secretExistsResponse.ts +++ b/src/api/model/secretExistsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/service.ts b/src/api/model/service.ts index dd0de897..2bbc4707 100644 --- a/src/api/model/service.ts +++ b/src/api/model/service.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/statistics.ts b/src/api/model/statistics.ts index 4bdfbe88..ff694286 100644 --- a/src/api/model/statistics.ts +++ b/src/api/model/statistics.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/streamResultOfLogStreamResponse.ts b/src/api/model/streamResultOfLogStreamResponse.ts index 4b95d556..3f20fbe4 100644 --- a/src/api/model/streamResultOfLogStreamResponse.ts +++ b/src/api/model/streamResultOfLogStreamResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/streamResultOfWorkflowExecution.ts b/src/api/model/streamResultOfWorkflowExecution.ts index 4d1834bb..935f08ac 100644 --- a/src/api/model/streamResultOfWorkflowExecution.ts +++ b/src/api/model/streamResultOfWorkflowExecution.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/updateSecretKeyValueResponse.ts b/src/api/model/updateSecretKeyValueResponse.ts index 3b25f23c..aa53c54e 100644 --- a/src/api/model/updateSecretKeyValueResponse.ts +++ b/src/api/model/updateSecretKeyValueResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/updateWorkflowExecutionsMetricsRequest.ts b/src/api/model/updateWorkflowExecutionsMetricsRequest.ts index 68a55b3c..987874c9 100644 --- a/src/api/model/updateWorkflowExecutionsMetricsRequest.ts +++ b/src/api/model/updateWorkflowExecutionsMetricsRequest.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/updateWorkspaceBody.ts b/src/api/model/updateWorkspaceBody.ts index d602afc7..00b83184 100644 --- a/src/api/model/updateWorkspaceBody.ts +++ b/src/api/model/updateWorkspaceBody.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowExecution.ts b/src/api/model/workflowExecution.ts index fc576297..0649d1f7 100644 --- a/src/api/model/workflowExecution.ts +++ b/src/api/model/workflowExecution.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowExecutionMetadata.ts b/src/api/model/workflowExecutionMetadata.ts index 646116b0..60e4fe9a 100644 --- a/src/api/model/workflowExecutionMetadata.ts +++ b/src/api/model/workflowExecutionMetadata.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowExecutionStatisticReport.ts b/src/api/model/workflowExecutionStatisticReport.ts index ee13af4b..834f2c7f 100644 --- a/src/api/model/workflowExecutionStatisticReport.ts +++ b/src/api/model/workflowExecutionStatisticReport.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowExecutionStatus.ts b/src/api/model/workflowExecutionStatus.ts index d7389851..30600bb6 100644 --- a/src/api/model/workflowExecutionStatus.ts +++ b/src/api/model/workflowExecutionStatus.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowExecutionsMetricsResponse.ts b/src/api/model/workflowExecutionsMetricsResponse.ts index 6885b0c2..4b218c96 100644 --- a/src/api/model/workflowExecutionsMetricsResponse.ts +++ b/src/api/model/workflowExecutionsMetricsResponse.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workflowTemplate.ts b/src/api/model/workflowTemplate.ts index ecb136e2..78b27277 100644 --- a/src/api/model/workflowTemplate.ts +++ b/src/api/model/workflowTemplate.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workspace.ts b/src/api/model/workspace.ts index 3a442cb2..8ee3043a 100644 --- a/src/api/model/workspace.ts +++ b/src/api/model/workspace.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,6 +13,7 @@ import { KeyValue } from './keyValue'; import { WorkspaceTemplate } from './workspaceTemplate'; import { Parameter } from './parameter'; import { WorkspaceStatus } from './workspaceStatus'; +import { WorkspaceComponent } from './workspaceComponent'; export interface Workspace { @@ -26,5 +27,6 @@ export interface Workspace { labels?: Array; url?: string; templateParameters?: Array; + workspaceComponents?: Array; } diff --git a/src/api/model/workspaceComponent.ts b/src/api/model/workspaceComponent.ts new file mode 100644 index 00000000..f393b257 --- /dev/null +++ b/src/api/model/workspaceComponent.ts @@ -0,0 +1,18 @@ +/** + * Onepanel + * Onepanel API + * + * The version of the OpenAPI document: 0.18.0 + * + * + * 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 WorkspaceComponent { + name?: string; + url?: string; +} + diff --git a/src/api/model/workspaceStatisticReport.ts b/src/api/model/workspaceStatisticReport.ts index 1ce1e5c0..9e1f5bfe 100644 --- a/src/api/model/workspaceStatisticReport.ts +++ b/src/api/model/workspaceStatisticReport.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workspaceStatus.ts b/src/api/model/workspaceStatus.ts index a4179ffa..2cdd7c83 100644 --- a/src/api/model/workspaceStatus.ts +++ b/src/api/model/workspaceStatus.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/api/model/workspaceTemplate.ts b/src/api/model/workspaceTemplate.ts index bb6a268b..de68c27b 100644 --- a/src/api/model/workspaceTemplate.ts +++ b/src/api/model/workspaceTemplate.ts @@ -2,7 +2,7 @@ * Onepanel * Onepanel API * - * The version of the OpenAPI document: 0.17.0 + * The version of the OpenAPI document: 0.18.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/src/app/alert/alert-panel/alert-panel.component.html b/src/app/alert/alert-panel/alert-panel.component.html index c8155f7c..239e8a0d 100644 --- a/src/app/alert/alert-panel/alert-panel.component.html +++ b/src/app/alert/alert-panel/alert-panel.component.html @@ -1,9 +1,10 @@ -
+
+ (alertAction)="onAlertAction($event)"> +
diff --git a/src/app/alert/alert-panel/alert-panel.component.ts b/src/app/alert/alert-panel/alert-panel.component.ts index 2f992409..3f054119 100644 --- a/src/app/alert/alert-panel/alert-panel.component.ts +++ b/src/app/alert/alert-panel/alert-panel.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Subscription } from "rxjs"; -import { AlertService } from "../alert.service"; -import { Alert } from "../alert"; -import { AlertActionEvent } from "../alert/alert.component"; +import { Subscription } from 'rxjs'; +import { AlertService } from '../alert.service'; +import { Alert } from '../alert'; +import { AlertActionEvent } from '../alert/alert.component'; interface AlertDisplayConfig { autoDismiss?: boolean; @@ -25,19 +25,43 @@ interface AlertWrapper { styleUrls: ['./alert-panel.component.scss'] }) export class AlertPanelComponent implements OnInit { + extraClasses = {}; + @Input() autoDismiss = false; @Input() autoDismissDelay = 5000; @Input() showCloseButton = true; @Input() showIcon = false; @Input() wipeOld = true; + @Input() classOnVisible = ''; + + constructor( + private alertService: AlertService, + ) { + } private subscribeReference: Subscription = null; alerts = Array(); - constructor( - private alertService: AlertService, - ) { + static fillInDefaultConfig(config?: AlertDisplayConfig): AlertDisplayConfig { + const finalConfig = { + autoDismiss: false, + autoDismissDelay: 5000, + showCloseButton: true, + showIcon: true, + }; + + if (config) { + finalConfig.autoDismiss = finalConfig.autoDismiss || config.autoDismiss; + finalConfig.showCloseButton = finalConfig.showCloseButton || config.showCloseButton; + finalConfig.showIcon = finalConfig.showIcon || config.showIcon; + + if (config.autoDismissDelay) { + finalConfig.autoDismissDelay = config.autoDismissDelay; + } + } + + return finalConfig; } ngOnInit() { @@ -51,6 +75,12 @@ export class AlertPanelComponent implements OnInit { newAlerts.unshift(...this.alerts); } + if (newAlerts.length !== 0) { + this.extraClasses = this.classOnVisible; + } else { + this.extraClasses = ''; + } + this.alerts = newAlerts; }, (_failure) => { @@ -86,7 +116,7 @@ export class AlertPanelComponent implements OnInit { const newAlerts = this.alerts.slice(); const wrapper = { - alert: alert, + alert, config: AlertPanelComponent.fillInDefaultConfig(config) }; @@ -99,7 +129,7 @@ export class AlertPanelComponent implements OnInit { const newAlerts = this.alerts.slice(); const wrapper = { - alert: alert, + alert, config: AlertPanelComponent.fillInDefaultConfig(config) }; @@ -112,7 +142,7 @@ export class AlertPanelComponent implements OnInit { const newAlerts = this.alerts.slice(); const wrapper = { - alert: alert, + alert, config: AlertPanelComponent.fillInDefaultConfig(config) }; @@ -127,7 +157,7 @@ export class AlertPanelComponent implements OnInit { for (const alert of alerts) { const wrapper = { - alert: alert, + alert, config: { autoDismiss: this.autoDismiss, autoDismissDelay: this.autoDismissDelay, @@ -141,25 +171,4 @@ export class AlertPanelComponent implements OnInit { return wrapped; } - - static fillInDefaultConfig(config?: AlertDisplayConfig): AlertDisplayConfig { - let finalConfig = { - autoDismiss: false, - autoDismissDelay: 5000, - showCloseButton: true, - showIcon: true, - }; - - if(config) { - finalConfig.autoDismiss = finalConfig.autoDismiss || config.autoDismiss; - finalConfig.showCloseButton = finalConfig.showCloseButton || config.showCloseButton; - finalConfig.showIcon = finalConfig.showIcon || config.showIcon; - - if(config.autoDismissDelay) { - finalConfig.autoDismissDelay = config.autoDismissDelay; - } - } - - return finalConfig; - } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 36fae86b..960e6985 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -133,6 +133,9 @@ import { ErrorComponent } from './error/error.component'; import { MetricsEditComponent } from './metrics/metrics-edit/metrics-edit.component'; import { MetricsEditDialogComponent } from './metrics/metrics-edit-dialog/metrics-edit-dialog.component'; import { DateFromUtcPipe } from './pipes/date-from-utc/date-from-utc.pipe'; +import { FileSyncComponent } from './file-sync/file-sync.component'; +import { FileBrowserDialogComponent } from './files/file-browser-dialog/file-browser-dialog.component'; +import { SimpleLogComponent } from './simple-log/simple-log.component'; @NgModule({ declarations: [ @@ -236,6 +239,9 @@ import { DateFromUtcPipe } from './pipes/date-from-utc/date-from-utc.pipe'; ErrorComponent, MetricsEditComponent, DateFromUtcPipe, + FileSyncComponent, + FileBrowserDialogComponent, + SimpleLogComponent ], entryComponents: [ WorkflowExecuteDialogComponent, @@ -245,7 +251,8 @@ import { DateFromUtcPipe } from './pipes/date-from-utc/date-from-utc.pipe'; LabelEditDialogComponent, MetricsEditDialogComponent, CreateNamespaceDialogComponent, - WorkspaceExecuteDialogComponent + WorkspaceExecuteDialogComponent, + FileBrowserDialogComponent ], imports: [ BrowserModule, diff --git a/src/app/file-sync/file-sync.component.html b/src/app/file-sync/file-sync.component.html new file mode 100644 index 00000000..b253e087 --- /dev/null +++ b/src/app/file-sync/file-sync.component.html @@ -0,0 +1,45 @@ +
+
+
Sync files
+
+ +
+ + + {{path}}/ + + + + +
+ + +
+
+
+
+
+ +
+ + + + +
+
+
+ Delete files in destination if they don't exist in origin +
+
+ + +
+
+ + + +
diff --git a/src/app/file-sync/file-sync.component.scss b/src/app/file-sync/file-sync.component.scss new file mode 100644 index 00000000..ed11cf44 --- /dev/null +++ b/src/app/file-sync/file-sync.component.scss @@ -0,0 +1,35 @@ +@import '../../styles/colors'; + +.file-sync { + .primary-border { + &.mat-stroked-button:not([disabled]) { + border-color: $primary; + } + } + + .mount-path { + width: 100px; + } + + .app-simple-log { + position: fixed; + width: 60vw; + right: 0; + top: 0; + height: 100vh; + z-index: 1000; + } + + .no-left { + /deep/ .mat-form-field-outline-start { + display: none; + } + + left: -3px; + } + + .fix-bottom { + position: relative; + bottom: 5px; + } +} diff --git a/src/app/file-sync/file-sync.component.spec.ts b/src/app/file-sync/file-sync.component.spec.ts new file mode 100644 index 00000000..fcfda92d --- /dev/null +++ b/src/app/file-sync/file-sync.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileSyncComponent } from './file-sync.component'; + +describe('FileSyncComponent', () => { + let component: FileSyncComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FileSyncComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FileSyncComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/file-sync/file-sync.component.ts b/src/app/file-sync/file-sync.component.ts new file mode 100644 index 00000000..e4375dc6 --- /dev/null +++ b/src/app/file-sync/file-sync.component.ts @@ -0,0 +1,203 @@ +import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Workspace, WorkspaceComponent, WorkspaceServiceService } from '../../api'; +import * as yaml from 'js-yaml'; +import { MatDialog } from '@angular/material/dialog'; +import { FileBrowserDialogComponent, FileBrowserDialogData } from '../files/file-browser-dialog/file-browser-dialog.component'; +import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms'; +import { HttpClient } from '@angular/common/http'; +import { AuthService } from '../auth/auth.service'; +import { SimpleLogComponent } from '../simple-log/simple-log.component'; +import { environment } from '../../environments/environment'; + +type syncAction = 'upload' | 'download'; + +interface SyncBody { + action: syncAction; + path: string; + delete: boolean; + prefix: string; +} + +@Component({ + selector: 'app-file-sync', + templateUrl: './file-sync.component.html', + styleUrls: ['./file-sync.component.scss'] +}) +export class FileSyncComponent implements OnInit { + @Input() namespace: string; + @Input() workspace: Workspace; + @ViewChild(SimpleLogComponent, {static: false}) log: SimpleLogComponent; + @ViewChild('objectStorageInput', {static: true}) objectStorageInput: ElementRef; + + form: FormGroup; + objectStoragePath: AbstractControl; + mountPath: AbstractControl; + mountPathInput: AbstractControl; + deleteFilesInDestination: AbstractControl; + + mountPaths: string[] = []; + showLogs = false; + + constructor( + private authService: AuthService, + private dialog: MatDialog, + private formBuilder: FormBuilder, + private httpClient: HttpClient) { } + + ngOnInit() { + const volumeMounts = this.parseVolumeMountsFromManifest(this.workspace.workspaceTemplate.manifest); + const mountPaths = []; + for (const volumeMount of volumeMounts) { + mountPaths.push(volumeMount.mountPath); + } + + this.mountPaths = mountPaths; + + this.form = this.formBuilder.group({ + objectStoragePath: [], + mountPath: [], + mountPathInput: [], + deleteFilesInDestination: [], + }); + + this.objectStoragePath = this.form.get('objectStoragePath'); + this.mountPath = this.form.get('mountPath'); + this.mountPathInput = this.form.get('mountPathInput'); + this.deleteFilesInDestination = this.form.get('deleteFilesInDestination'); + this.deleteFilesInDestination.setValue(false); + + if (mountPaths.length !== 0) { + this.mountPath.setValue(mountPaths[0]); + } + + this.objectStoragePath.setValue('/'); + } + + parseVolumeMountsFromManifest(manifest: string): Array<{name: string, mountPath: string}> { + const data = yaml.safeLoad(manifest); + const fileSyncerContainer = data.containers.find(container => container.name === 'sys-filesyncer'); + const volumeMounts = []; + + for (const volumeMount of fileSyncerContainer.volumeMounts) { + if (volumeMount.name.indexOf('sys-') > -1) { + continue; + } + + volumeMounts.push(volumeMount); + } + + return volumeMounts; + } + + handleBrowse() { + let path: string = this.objectStoragePath.value; + if (!path) { + path = '/'; + } + + if (path !== '/' && path.startsWith('/')) { + path = path.substring(1); + } + + const data: FileBrowserDialogData = { + namespace: this.namespace, + path, + }; + + const dialog = this.dialog.open(FileBrowserDialogComponent, { + width: '60vw', + maxHeight: '100vh', + data + }); + + dialog.afterClosed().subscribe((res: string|undefined) => { + if (!res) { + return; + } + + this.objectStoragePath.setValue(res); + }); + } + + private getFinalObjectStoragePath() { + // Skip the first character because it will be a '/' and API doesn't want that. + return this.objectStoragePath.value.substring(1); + } + + private getPostData(action: syncAction): SyncBody { + let path = this.mountPath.value + '/'; + if (this.mountPathInput.value) { + path += this.mountPathInput.value; + } + + return { + action, + path, + delete: this.deleteFilesInDestination.value, + prefix: this.getFinalObjectStoragePath() + }; + } + + private syncRequest(body: SyncBody) { + const filesyncer = this.workspace.workspaceComponents.find((workspace: WorkspaceComponent) => { + return workspace.name === 'sys-filesyncer'; + }); + + const url = filesyncer.url + '/api/sync'; + + return this.httpClient.post(url, body, + { + headers: { + 'onepanel-auth-token': this.authService.getAuthToken() + } + }); + } + + handleSyncToObjectStorage() { + const now = new Date(); + + const body = this.getPostData('upload'); + this.syncRequest(body).subscribe(res => { + this.watchLogs(now); + }, err => { + console.error(err); + }); + } + + handleSyncToWorkspace() { + const now = new Date(); + + const body = this.getPostData('download'); + this.syncRequest(body).subscribe(res => { + this.watchLogs(now); + }, err => { + console.error(err); + }); + } + + /** + * @param sinceTime timestamp indicating from what point the logs should be watched. + */ + watchLogs(sinceTime: Date) { + this.showLogs = true; + const sinceEpochSeconds = Math.floor(sinceTime.getTime() / 1000); + const url = `${environment.baseWsUrl}/apis/v1beta1/${this.namespace}/workspaces/${this.workspace.uid}/containers/sys-filesyncer/logs?sinceTime=${sinceEpochSeconds}`; + this.log.start(url); + } + + handleLogsClose() { + this.log.clear(); + setTimeout(() => { + this.showLogs = false; + }, 100); + } + + handleStopEditObjectStoragePath() { + const path: string = this.objectStoragePath.value; + if (!path) { + this.objectStoragePath.setValue('/'); + } else if (!path.endsWith('/')) { + this.objectStoragePath.setValue(path + '/'); + } + } +} diff --git a/src/app/files/file-browser-dialog/file-browser-dialog.component.html b/src/app/files/file-browser-dialog/file-browser-dialog.component.html new file mode 100644 index 00000000..39859413 --- /dev/null +++ b/src/app/files/file-browser-dialog/file-browser-dialog.component.html @@ -0,0 +1,15 @@ +
Select folder
+ + + + + + + + Confirm + diff --git a/src/app/files/file-browser-dialog/file-browser-dialog.component.scss b/src/app/files/file-browser-dialog/file-browser-dialog.component.scss new file mode 100644 index 00000000..512a62fc --- /dev/null +++ b/src/app/files/file-browser-dialog/file-browser-dialog.component.scss @@ -0,0 +1,10 @@ +.mat-dialog-content-height { + height: 65vh; +} + +.file-sync-file-browser { + /deep/ .toolbar-wrapper { + position: sticky; + top: 0; + } +} diff --git a/src/app/files/file-browser-dialog/file-browser-dialog.component.ts b/src/app/files/file-browser-dialog/file-browser-dialog.component.ts new file mode 100644 index 00000000..f78dea10 --- /dev/null +++ b/src/app/files/file-browser-dialog/file-browser-dialog.component.ts @@ -0,0 +1,68 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FileNavigator } from '../fileNavigator'; +import { WorkflowServiceService } from '../../../api'; +import { AlertService } from '../../alert/alert.service'; +import { Alert } from '../../alert/alert'; + +export interface FileBrowserDialogData { + namespace: string; + path: string; + displayRootPath?: string; +} + +@Component({ + selector: 'app-filebrowser-dialog', + templateUrl: './file-browser-dialog.component.html', + styleUrls: ['./file-browser-dialog.component.scss'] +}) +export class FileBrowserDialogComponent implements OnInit { + fileNavigator: FileNavigator; + namespace: string; + + constructor( + private alertService: AlertService, + private workflowServiceService: WorkflowServiceService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: FileBrowserDialogData + ) { + this.namespace = data.namespace; + + this.fileNavigator = new FileNavigator({ + rootPath: '/', + path: data.path, + namespace: data.namespace, + name: 'dialog', + workflowService: this.workflowServiceService, + displayRootPath: data.displayRootPath, + }); + } + + ngOnInit() { + + } + + handleCancel() { + this.dialogRef.close(); + } + + handleConfirm() { + const file = this.fileNavigator.file.value; + + // If you do not change the directory while in the browser, the root is not considered a directory + // but it is, so we need to return the result as just the root. + if (file.path === '/') { + this.dialogRef.close( '/'); + return; + } else if (!file.directory) { + this.alertService.storeAlert(new Alert({ + type: 'danger', + message: 'Current selection is a file, not a folder' + })); + + return; + } + + this.dialogRef.close( '/' + file.path); + } +} diff --git a/src/app/files/file-browser/file-browser.component.html b/src/app/files/file-browser/file-browser.component.html index e577d7ec..0aae7ea3 100644 --- a/src/app/files/file-browser/file-browser.component.html +++ b/src/app/files/file-browser/file-browser.component.html @@ -1,19 +1,22 @@ -
-
-
Location:
-
- {{rootName}} - / +
+
+
+
Location:
+
+ {{rootName}} + / +
+ + +
- - -
diff --git a/src/app/files/file-browser/file-browser.component.scss b/src/app/files/file-browser/file-browser.component.scss index 178bff60..1671dc18 100644 --- a/src/app/files/file-browser/file-browser.component.scss +++ b/src/app/files/file-browser/file-browser.component.scss @@ -1,8 +1,20 @@ @import "../../../styles/colors"; +$borderColor: #c1c1c1; + +// We add this to allow "hiding" any overflowing content +// e.g. if parent makes .toolbar sticky, the borders will show scrolling up +// this wrapper will hide them +.toolbar-wrapper { + background: white; +} + .toolbar { padding: 5px 20px; min-height: 36px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border: solid 1px $borderColor; } .lh-36 { @@ -51,4 +63,13 @@ mat-spinner { border-top: 1px solid; border-color: $light-white; padding: 0 20px; + border-left: solid 1px $borderColor; + border-right: solid 1px $borderColor; +} + +.file-navigator { + /deep/ .file-list-item { + border-left: solid 1px $borderColor; + border-right: solid 1px $borderColor; + } } diff --git a/src/app/files/file-browser/file-browser.component.ts b/src/app/files/file-browser/file-browser.component.ts index 88ead5e6..39e5bb6c 100644 --- a/src/app/files/file-browser/file-browser.component.ts +++ b/src/app/files/file-browser/file-browser.component.ts @@ -13,6 +13,7 @@ import { GenericFileViewComponent } from '../file-viewer/generic-file-view/gener export class FileBrowserComponent implements OnInit, OnDestroy { private filePathChangedSubscriber; private fileChangedSubscriber; + private changingFilesSubscriber; // tslint:disable-next-line:variable-name private _fileNavigator: FileNavigator; @@ -31,6 +32,26 @@ export class FileBrowserComponent implements OnInit, OnDestroy { this.filePathChangedSubscriber.unsubscribe(); } + this.changingFilesSubscriber = value.changingFiles.valueChanged.subscribe((change: SlowValueUpdate>) => { + if (change.state === LongRunningTaskState.Started) { + setTimeout(() => { + this.loading = true; + }); + } + + if (change.state === LongRunningTaskState.Succeeded) { + setTimeout(() => { + this.loading = false; + }); + } + + if (change.state === LongRunningTaskState.Failed) { + setTimeout(() => { + this.loading = false; + }); + } + }); + this.filePathChangedSubscriber = value.path.valueChanged.subscribe((change: SlowValueUpdate) => { if (change.state === LongRunningTaskState.Succeeded) { this.updatePathParts(change.value); @@ -78,7 +99,11 @@ export class FileBrowserComponent implements OnInit, OnDestroy { } updatePathParts(path: string) { - const subPath = path.substring(this.fileNavigator.rootPath.length); + let subPath = path; + if (this.fileNavigator.rootPath !== '/') { + subPath = path.substring(this.fileNavigator.rootPath.length); + } + const newParts = subPath.split('/'); this.pathParts = newParts.filter( value => value !== ''); } @@ -110,12 +135,18 @@ export class FileBrowserComponent implements OnInit, OnDestroy { private getPathFromBreadcrumbIndex(index: number): string { const path = this.fileNavigator.path.value; - const subPath = path.substring(this.fileNavigator.rootPath.length); - - const parts = subPath.split('/').filter(value => value.length != 0); + let subPath = path; + if (this.fileNavigator.rootPath !== '/') { + subPath = path.substring(this.fileNavigator.rootPath.length); + } + const parts = subPath.split('/').filter(value => value.length !== 0); const partUntil = parts.slice(0, index + 1).join('/'); + if (this.fileNavigator.rootPath === '/') { + return partUntil; + } + return this.fileNavigator.rootPath + '/' + partUntil; } diff --git a/src/app/files/file-navigator/file-navigator.component.html b/src/app/files/file-navigator/file-navigator.component.html index 086b0b97..d459851a 100644 --- a/src/app/files/file-navigator/file-navigator.component.html +++ b/src/app/files/file-navigator/file-navigator.component.html @@ -24,7 +24,7 @@ -
{{file.size | fileSize}}
+
{{file.size | fileSize}}
@@ -38,6 +38,6 @@ + class="d-flex align-items-center font-size-regular font-roboto file-list-item"> diff --git a/src/app/files/file-navigator/file-navigator.component.scss b/src/app/files/file-navigator/file-navigator.component.scss index 794e541b..910c95cb 100644 --- a/src/app/files/file-navigator/file-navigator.component.scss +++ b/src/app/files/file-navigator/file-navigator.component.scss @@ -6,11 +6,16 @@ .cdk-row { height: 40px; padding: 0 20px; - border-top: 1px solid #c1c1c1; + border-bottom: 1px solid #c1c1c1; &:first-of-type { } + &:last-of-type { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + &:hover { background-color: #f6f8fa; } diff --git a/src/app/files/file-navigator/file-navigator.component.ts b/src/app/files/file-navigator/file-navigator.component.ts index 685b8f0f..c914924d 100644 --- a/src/app/files/file-navigator/file-navigator.component.ts +++ b/src/app/files/file-navigator/file-navigator.component.ts @@ -1,59 +1,61 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FileNavigator } from "../fileNavigator"; -import { ModelFile } from "../../../api"; +import { FileNavigator } from '../fileNavigator'; +import { ModelFile } from '../../../api'; import { GenericFileViewComponent } from '../file-viewer/generic-file-view/generic-file-view.component'; export type FileAction = 'download'; export interface FileActionEvent { - action: FileAction; - file: ModelFile; + action: FileAction; + file: ModelFile; } @Component({ - selector: 'app-file-navigator', - templateUrl: './file-navigator.component.html', - styleUrls: ['./file-navigator.component.scss'] + selector: 'app-file-navigator', + templateUrl: './file-navigator.component.html', + styleUrls: ['./file-navigator.component.scss'] }) export class FileNavigatorComponent implements OnInit { - @Input() displayedColumns = ['icon', 'name', 'last-modified', 'size', 'actions']; + @Input() displayedColumns = ['icon', 'name', 'last-modified', 'size', 'actions']; - // tslint:disable-next-line:variable-name - private _fileNavigator: FileNavigator; + // tslint:disable-next-line:variable-name + private _fileNavigator: FileNavigator; + @Input() set fileNavigator(fileNavigator: FileNavigator) { + if (!fileNavigator) { + return; + } - @Input() set fileNavigator(fileNavigator: FileNavigator) { - if (!fileNavigator) { - return; + this._fileNavigator = fileNavigator; + this._fileNavigator.loadFiles(); } - this._fileNavigator = fileNavigator; - this._fileNavigator.loadFiles(); - } - get fileNavigator(): FileNavigator { - return this._fileNavigator; - } + get fileNavigator(): FileNavigator { + return this._fileNavigator; + } - @Output() fileAction = new EventEmitter(); - constructor() { } + @Output() fileAction = new EventEmitter(); - ngOnInit() { - } + constructor() { + } - onFileClick(file: ModelFile) { - this.fileNavigator.selectFile(file); - } + ngOnInit() { + } - onFileDownload(file: ModelFile) { - this.fileAction.emit({ - action: 'download', - file, - }); - } + onFileClick(file: ModelFile) { + this.fileNavigator.selectFile(file); + } - canDownload(file: ModelFile) { - return !file.directory && + onFileDownload(file: ModelFile) { + this.fileAction.emit({ + action: 'download', + file, + }); + } + + canDownload(file: ModelFile) { + return !file.directory && parseInt(file.size, 10) < GenericFileViewComponent.MAX_DOWNLOAD_SIZE - ; - } + ; + } } diff --git a/src/app/files/file-viewer/generic-file-view/generic-file-view.component.html b/src/app/files/file-viewer/generic-file-view/generic-file-view.component.html index bb33299e..520b796e 100644 --- a/src/app/files/file-viewer/generic-file-view/generic-file-view.component.html +++ b/src/app/files/file-viewer/generic-file-view/generic-file-view.component.html @@ -8,8 +8,8 @@ [namespace]="namespace" [name]="name" [file]="_file" - (loading)="onLoadingChange($event)" - > + (loading)="onLoadingChange($event)"> + GenericFileViewComponent.MAX_IMAGE_FILE_SIZE; - } + fileType = 'unknown'; - return size > GenericFileViewComponent.BIG_FILE_SIZE; - } + // tslint:disable-next-line:variable-name + _file: ModelFile = null; + @Input() showDownload = true; + @Output() actionClick = new EventEmitter(); - fileType = 'unknown'; + public static IsFileTooBigToDisplay(file: ModelFile): boolean { + let size = 0; + if (file.size) { + size = parseInt(file.size, 10); + } - // tslint:disable-next-line:variable-name - _file: ModelFile = null; + if (ImageFileViewComponent.Supports(file)) { + return size > GenericFileViewComponent.MAX_IMAGE_FILE_SIZE; + } - @Input() showDownload = true; - @Output() actionClick = new EventEmitter(); + return size > GenericFileViewComponent.BIG_FILE_SIZE; + } - constructor() {} + constructor() { + } - @Input() set file(file: ModelFile) { - this._file = file; - this.fileType = this.getFileType(); - } + @Input() set file(file: ModelFile) { + this._file = file; + this.fileType = this.getFileType(); + } - @Input() namespace: string; - @Input() name: string; - @Output() loading = new EventEmitter(); + @Input() namespace: string; + @Input() name: string; + @Output() loading = new EventEmitter(); - getFileType(): string { - this.loading.emit(false); - const extension = this._file.extension; + getFileType(): string { + this.loading.emit(false); + const extension = this._file.extension; - if (GenericFileViewComponent.IsFileTooBigToDisplay(this._file)) { - return 'big-file'; - } + if (GenericFileViewComponent.IsFileTooBigToDisplay(this._file)) { + return 'big-file'; + } - if (extension.includes('zip') || extension.includes('gz')) { - return 'big-file'; - } + if (extension.includes('zip') || extension.includes('gz')) { + return 'big-file'; + } - if (ImageFileViewComponent.Supports(this._file)) { - return 'image'; - } + if (ImageFileViewComponent.Supports(this._file)) { + return 'image'; + } - if (TextFileViewComponent.Supports(this._file)) { - return 'text'; - } + if (TextFileViewComponent.Supports(this._file)) { + return 'text'; + } - return 'big-file'; - } + return 'big-file'; + } - onLoadingChange(value: boolean) { - this.loading.emit(value); - } + onLoadingChange(value: boolean) { + this.loading.emit(value); + } - onFileAction(e: FileActionEvent) { - this.actionClick.emit(e); - } + onFileAction(e: FileActionEvent) { + this.actionClick.emit(e); + } } diff --git a/src/app/files/fileNavigator.ts b/src/app/files/fileNavigator.ts index 39dc0ff8..bcc9a393 100644 --- a/src/app/files/fileNavigator.ts +++ b/src/app/files/fileNavigator.ts @@ -77,6 +77,7 @@ export class SlowValue { export interface FileNavigatorArgs { workflowService: WorkflowServiceService; rootPath: string; + path?: string; displayRootPath?: string; directory?: boolean; namespace: string; @@ -95,6 +96,7 @@ export class FileNavigator { displayRootPath: string; path: SlowValue; file: SlowValue; + changingFiles: SlowValue>; files?: Array; @Output() filesChanged = new EventEmitter(); @@ -108,12 +110,15 @@ export class FileNavigator { this.displayRootPath = args.rootPath; } - this.path = new SlowValue(args.rootPath); + const initialPath = args.path ? args.path : args.rootPath; + + this.path = new SlowValue(initialPath); this.file = new SlowValue({ path: args.rootPath, directory: args.directory ? args.directory : false, }); + this.changingFiles = new SlowValue>([]); this.namespace = args.namespace; this.name = args.name; @@ -179,6 +184,8 @@ export class FileNavigator { loadFiles(file?: ModelFile) { if (file) { this.file.requestValueChange(); + } else { + this.changingFiles.requestValueChange(); } this.workflowService.listFiles(this.namespace, this.name, this.path.value) @@ -215,6 +222,7 @@ export class FileNavigator { } this.files = response.files; + this.changingFiles.reportValueChange(response.files); this.filesChanged.emit(); if (file) { @@ -223,7 +231,6 @@ export class FileNavigator { }); } - cleanUp() { } } diff --git a/src/app/simple-log/simple-log.component.html b/src/app/simple-log/simple-log.component.html new file mode 100644 index 00000000..05db2023 --- /dev/null +++ b/src/app/simple-log/simple-log.component.html @@ -0,0 +1,25 @@ +
+
+ +
+ LOGS +
+
+ {{information}} +
+ +
+ +
diff --git a/src/app/simple-log/simple-log.component.scss b/src/app/simple-log/simple-log.component.scss new file mode 100644 index 00000000..7c071c90 --- /dev/null +++ b/src/app/simple-log/simple-log.component.scss @@ -0,0 +1,35 @@ +.logs-root { + background-color: rgb(47, 49, 41); + height: 100%; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + + .toolbar { + padding-left: 27px; + } + + .title { + strong { + font-weight: bold; + } + } + + .loading { + width: 20px; + height: 20px; + padding-top: 3px; + margin-right: 10px; + margin-left: -10px; + } + + [ace-editor] { + height: 100%; + } + + /deep/ .ace-monokai { + // remove the error background color as we want to display the log as plain text, no special colors. + .ace_invalid { + background-color: unset; + } + } +} diff --git a/src/app/simple-log/simple-log.component.spec.ts b/src/app/simple-log/simple-log.component.spec.ts new file mode 100644 index 00000000..2a965fdf --- /dev/null +++ b/src/app/simple-log/simple-log.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SimpleLogComponent } from './simple-log.component'; + +describe('SimpleLogComponent', () => { + let component: SimpleLogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SimpleLogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleLogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/simple-log/simple-log.component.ts b/src/app/simple-log/simple-log.component.ts new file mode 100644 index 00000000..30a88c9a --- /dev/null +++ b/src/app/simple-log/simple-log.component.ts @@ -0,0 +1,309 @@ +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { AceEditorComponent } from 'ng2-ace-editor'; +import { WorkflowService } from '../workflow/workflow.service'; +import { strings } from '../../utility/strings'; + +@Component({ + selector: 'app-simple-log', + templateUrl: './simple-log.component.html', + styleUrls: ['./simple-log.component.scss'] +}) +export class SimpleLogComponent implements OnInit, OnDestroy { + @Input() noLoading = false; + + private socket: WebSocket; + private biggestScrollDown = 0; + private biggestScrollUp = 0; + + // tslint:disable-next-line:variable-name + private _aceEditor; + + @ViewChild(AceEditorComponent, {static: false}) set aceEditor(aceEditor: AceEditorComponent) { + this._aceEditor = aceEditor; + + if (aceEditor) { + aceEditor.getEditor().session.on('changeScrollTop', (e) => { + // Ignore scrolling above the scroll area. + if (e < 0) { + return; + } + + if (this.lastScroll) { + this.onScrollChange(e, this.lastScroll); + } + + this.lastScroll = e; + }); + } + } + + get aceEditor(): AceEditorComponent { + return this._aceEditor; + } + + /** + * socketUrl is a url that provides logs via websocket. + */ + socketUrl: string; + + /** + * Maximum number of lines to display in the log + */ + @Input() maxLines = 30000; + + /** + * How many lines we delete one the maxLines limit is reached. + * + * So if you have 100 max lines, and a linesChunkDelete of 10, once you hit 100 lines, the lines will be reduced to + * 90, deleting from the front. + */ + @Input() linesChunkDelete = 100; + @Output() closeClicked = new EventEmitter(); + + // The text displayed in the log. + logText = ''; + + // True if logs are loading data, false otherwise. + loading = true; + + // The text displayed in the information section of the toolbar. + information = ''; + + // Last scroll position. Bookkeeping variable to track scroll amounts. + lastScroll: number; + + // If true, the UI should scroll the user to the bottom as more log data is added. + scrollToBottom = true; + + // Utility to determine if we can change the scrollToBottom value. + canChangeScrollToBottom = true; + + // The number of lines of log text we have. + private lines = 0; + + // The total number of lines we have had. + private totalLines = 0; + + constructor() { } + + ngOnInit(): void { + } + + clearLog() { + this.logText = ''; + this.lines = 0; + this.totalLines = 0; + this.aceEditor.setText(''); + this.onLogsUpdated(); + } + + private appendTextToAceEditor(text: string) { + const session = this.aceEditor.getEditor().session; + session.insert({ + row: session.getLength(), + column: 0 + }, text); + } + + /** + * Append text to the log. + */ + writeLog(text: string) { + if (this.lines > this.maxLines) { + const indexCount = strings.findNthIndex(this.logText, '\n', this.linesChunkDelete); + + const index = indexCount.index; + const count = indexCount.count; + + if (index > -1) { + this.logText = this.logText.substr(index + 1); + + this.lines -= count; + + // keep track of the total lines, so the editor displays the correct line number + this.aceEditor.setOptions({ + firstLineNumber: this.totalLines - this.lines + 1 + }); + + this.aceEditor.setText(this.logText); + } + } + + this.appendTextToAceEditor(text); + + this.logText += text; + + // Weird, but fast? + // https://stackoverflow.com/questions/881085/count-the-number-of-occurrences-of-a-character-in-a-string-in-javascript + const newLineCount = (text.split('\n').length - 1); + this.lines += newLineCount; + this.totalLines += newLineCount; + + this.onLogsUpdated(); + } + + /** + * Append text to the log, and then append a newline. + */ + writeLogLn(text: string) { + this.writeLog(text + '\n'); + } + + start(url: string) { + this.socketUrl = url; + // We're switching to a new node/logs provider. + // Clean up the old subscription if there was one. + if (this.socket) { + this.socket.close(); + } + + // Clean up the log text as the new node/logs provider will have new logs. + this.clearLog(); + + this.loading = true; + + this.socket = new WebSocket(this.socketUrl); + this.socket.onmessage = (event) => { + try { + const jsonData = JSON.parse(event.data); + const logEntries = jsonData.result.logEntries; + + let bufferedString = ''; + for (let i = 0; i < logEntries.length; i++) { + const logEntry = logEntries[i]; + + if (logEntry.timestamp) { + bufferedString += `${logEntry.timestamp} ${logEntry.content}`; + } else { + bufferedString += logEntry.content; + } + + if (i < logEntries.length) { + bufferedString += '\n'; + } + } + + if (bufferedString !== '') { + this.writeLog(bufferedString); + } + } catch (e) { + console.error(e); + } + }; + + this.socket.onclose = () => { + this.loading = false; + }; + } + + clear() { + if (this.socket) { + this.socket.close(); + } + + this.clearLog(); + this.socketUrl = undefined; + this.loading = false; + this.information = ''; + } + + ngOnDestroy(): void { + if (this.socket) { + this.socket.close(); + } + } + + onCloseClick() { + this.closeClicked.emit(); + } + + onScrollChange(newScrollPosition: number, oldScrollPosition: number) { + const diff = newScrollPosition - oldScrollPosition; + + if (diff < this.biggestScrollUp) { + this.biggestScrollUp = diff; + } + + if (diff > this.biggestScrollDown) { + this.biggestScrollDown = diff; + } + + if (diff < 0 && diff < (this.biggestScrollUp * 0.70)) { + return; + } + + if (diff > 0 && diff > (this.biggestScrollDown * 0.70)) { + return; + } + + // Ignore scrolling in 'place' + if (diff !== 0) { + const scrollingUp = diff < 0; + + // When we scroll up, stop the automatic scrolling to the bottom of the logs. + if (scrollingUp) { + if (this.canChangeScrollToBottom) { + this.scrollToBottom = false; + } + } else { // Scrolling down + const lastVisibleRow = this.aceEditor.getEditor().getLastVisibleRow(); + const totalRows = this.aceEditor.getEditor().session.getLength(); + const rowsFromBottom = totalRows - lastVisibleRow; + + if (rowsFromBottom < 10) { + if (this.canChangeScrollToBottom) { + this.scrollToBottom = true; + } + this.canChangeScrollToBottom = false; + + // This is to handle the case where the system is scrolling us automatically up to the last line + // if we overscrolled. + setTimeout(() => { + this.canChangeScrollToBottom = true; + }, 500); + } + } + } + } + + editorHasLessContentThanScreenAllows(): boolean { + if (!this.aceEditor) { + return false; + } + + const editor = this.aceEditor.getEditor(); + + const totalRows = editor.session.getLength(); + const lastVisibleRow = editor.getLastVisibleRow(); + + return lastVisibleRow === totalRows; + } + + onLogsUpdated() { + this.scrollToBottomIfNeeded(); + } + + /** + * Scrolls to the bottom of the logs content if + * 1. There is more log content than visible. + * 2. The scrollToBottom variable is true. + */ + scrollToBottomIfNeeded() { + if (!this.aceEditor) { + return; + } + + if (this.editorHasLessContentThanScreenAllows()) { + return; + } + + if (!this.scrollToBottom) { + return; + } + + const numberLines = this.aceEditor.getEditor().session.getDocument().getLength(); + + this.aceEditor.getEditor().scrollToLine(numberLines, false, false); + } + +} diff --git a/src/app/workspace/workspace-view/workspace-view-parameters/workspace-view-parameters.component.html b/src/app/workspace/workspace-view/workspace-view-parameters/workspace-view-parameters.component.html index 7cfddc47..a18c29c2 100644 --- a/src/app/workspace/workspace-view/workspace-view-parameters/workspace-view-parameters.component.html +++ b/src/app/workspace/workspace-view/workspace-view-parameters/workspace-view-parameters.component.html @@ -1,6 +1,6 @@ -
+
-
diff --git a/src/app/workspace/workspace-view/workspace-view.component.scss b/src/app/workspace/workspace-view/workspace-view.component.scss index 15e526cf..af1c0ced 100644 --- a/src/app/workspace/workspace-view/workspace-view.component.scss +++ b/src/app/workspace/workspace-view/workspace-view.component.scss @@ -44,4 +44,11 @@ app-workspace-identifier { } .workspace-info { + .border-right { + border-right: 1px solid $gray; + } + + .third { + flex-basis: 33%; + } } diff --git a/src/app/workspace/workspace-view/workspace-view.component.ts b/src/app/workspace/workspace-view/workspace-view.component.ts index 440b2abb..6577b9e8 100644 --- a/src/app/workspace/workspace-view/workspace-view.component.ts +++ b/src/app/workspace/workspace-view/workspace-view.component.ts @@ -4,163 +4,163 @@ import { Parameter, UpdateWorkspaceBody, Workspace, WorkspaceServiceService } fr import { DomSanitizer, SafeResourceUrl, Title } from '@angular/platform-browser'; import { MatDialog } from '@angular/material/dialog'; import { - ConfirmationDialogComponent, - ConfirmationDialogData + ConfirmationDialogComponent, + ConfirmationDialogData } from '../../confirmation-dialog/confirmation-dialog.component'; import { AppRouter } from '../../router/app-router.service'; export type WorkspaceState = 'Launching' | 'Updating' | 'Pausing' | 'Paused' | 'Resuming' | 'Running' | 'Deleting'; @Component({ - selector: 'app-workspace-view', - templateUrl: './workspace-view.component.html', - styleUrls: ['./workspace-view.component.scss'] + selector: 'app-workspace-view', + templateUrl: './workspace-view.component.html', + styleUrls: ['./workspace-view.component.scss'] }) export class WorkspaceViewComponent implements OnInit, OnDestroy { - position = 'fixed'; - - hideNavigationBar = true; - - namespace: string; - workspaceUid: string; - workspace: Workspace; - workspaceUrl: SafeResourceUrl; - workspaceChecker: number; - state: WorkspaceState; - showWorkspaceDetails = false; - - parameters: Array = []; - - constructor( - private appRouter: AppRouter, - private activatedRoute: ActivatedRoute, - private domSanitizer: DomSanitizer, - private workspaceService: WorkspaceServiceService, - private dialog: MatDialog, - private title: Title, - ) { - this.activatedRoute.paramMap.subscribe(next => { - this.namespace = next.get('namespace'); - this.workspaceUid = next.get('uid'); - - this.startWorkspaceChecker(true); - }); - } - - ngOnInit() { - } - - ngOnDestroy() { - this.clearWorkspaceChecker(); - } - - private startWorkspaceChecker(runNow: boolean = true) { - this.clearWorkspaceChecker(); - - if (runNow) { - this.getWorkspace(); + position = 'fixed'; + + hideNavigationBar = true; + + namespace: string; + workspaceUid: string; + workspace: Workspace; + workspaceUrl: SafeResourceUrl; + workspaceChecker: number; + state: WorkspaceState; + showWorkspaceDetails = false; + + parameters: Array = []; + + constructor( + private appRouter: AppRouter, + private activatedRoute: ActivatedRoute, + private domSanitizer: DomSanitizer, + private workspaceService: WorkspaceServiceService, + private dialog: MatDialog, + private title: Title, + ) { + this.activatedRoute.paramMap.subscribe(next => { + this.namespace = next.get('namespace'); + this.workspaceUid = next.get('uid'); + + this.startWorkspaceChecker(true); + }); + } + + ngOnInit() { } - this.workspaceChecker = setInterval(() => { - this.getWorkspace(); - }, 5000); - } + ngOnDestroy() { + this.clearWorkspaceChecker(); + } + + private startWorkspaceChecker(runNow: boolean = true) { + this.clearWorkspaceChecker(); + + if (runNow) { + this.getWorkspace(); + } - private clearWorkspaceChecker() { - if (this.workspaceChecker) { - clearInterval(this.workspaceChecker); - this.workspaceChecker = null; + this.workspaceChecker = setInterval(() => { + this.getWorkspace(); + }, 5000); } - } - - getWorkspace() { - this.workspaceService.getWorkspace(this.namespace, this.workspaceUid).subscribe(res => { - if (res && !this.workspace) { - this.title.setTitle(`Onepanel - ${res.name}`); - } - this.workspace = res; - // We add a 't' query parameter is so we avoid caching the response. - this.workspaceUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(res.url + '?t=' + Date.now()); - - this.parameters = res.parameters; - - switch (res.status.phase) { - case 'Running': - this.state = 'Running'; - this.clearWorkspaceChecker(); - break; - case 'Paused': - this.state = 'Paused'; - this.clearWorkspaceChecker(); - break; - case 'Updating': - this.state = 'Updating'; - break; - } - }); - } - - onPause(workspace: Workspace) { - this.state = 'Pausing'; - this.workspaceService.pauseWorkspace(this.namespace, workspace.uid) - .subscribe(res => { - this.startWorkspaceChecker(true); + + private clearWorkspaceChecker() { + if (this.workspaceChecker) { + clearInterval(this.workspaceChecker); + this.workspaceChecker = null; + } + } + + getWorkspace() { + this.workspaceService.getWorkspace(this.namespace, this.workspaceUid).subscribe(res => { + if (res && !this.workspace) { + this.title.setTitle(`Onepanel - ${res.name}`); + } + this.workspace = res; + // We add a 't' query parameter is so we avoid caching the response. + this.workspaceUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(res.url + '?t=' + Date.now()); + + this.parameters = res.parameters; + + switch (res.status.phase) { + case 'Running': + this.state = 'Running'; + this.clearWorkspaceChecker(); + break; + case 'Paused': + this.state = 'Paused'; + this.clearWorkspaceChecker(); + break; + case 'Updating': + this.state = 'Updating'; + break; + } }); - } - - onDelete(workspace: Workspace) { - const data: ConfirmationDialogData = { - title: 'Are you sure you want to delete this workspace?', - confirmText: 'DELETE', - type: 'delete' - }; - - const dialogRef = this.dialog.open(ConfirmationDialogComponent, { - data - }); - - dialogRef.afterClosed().subscribe(result => { - if (!result) { - return; - } - - this.state = 'Deleting'; - this.workspaceService.deleteWorkspace(this.namespace, workspace.uid) - .subscribe(res => { - this.appRouter.navigateToWorkspaces(this.namespace); - }); - }); - - } - - onResume(workspace: Workspace) { - this.state = 'Resuming'; - this.workspaceService.resumeWorkspace(this.namespace, workspace.uid) - .subscribe(res => { - this.startWorkspaceChecker(true); + } + + onPause(workspace: Workspace) { + this.state = 'Pausing'; + this.workspaceService.pauseWorkspace(this.namespace, workspace.uid) + .subscribe(res => { + this.startWorkspaceChecker(true); + }); + } + + onDelete(workspace: Workspace) { + const data: ConfirmationDialogData = { + title: 'Are you sure you want to delete this workspace?', + confirmText: 'DELETE', + type: 'delete' + }; + + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data }); - } - onToggleWorkspaceDetails() { - this.showWorkspaceDetails = !this.showWorkspaceDetails; + dialogRef.afterClosed().subscribe(result => { + if (!result) { + return; + } + + this.state = 'Deleting'; + this.workspaceService.deleteWorkspace(this.namespace, workspace.uid) + .subscribe(res => { + this.appRouter.navigateToWorkspaces(this.namespace); + }); + }); - if (this.showWorkspaceDetails) { - this.position = 'relative'; - } else { - this.position = 'fixed'; } - } - onUpdateWorkspace(parameters: Array) { - this.state = 'Updating'; + onResume(workspace: Workspace) { + this.state = 'Resuming'; + this.workspaceService.resumeWorkspace(this.namespace, workspace.uid) + .subscribe(res => { + this.startWorkspaceChecker(true); + }); + } - const body: UpdateWorkspaceBody = { - parameters, - }; + onToggleWorkspaceDetails() { + this.showWorkspaceDetails = !this.showWorkspaceDetails; - this.workspaceService.updateWorkspace(this.namespace, this.workspace.uid, body) - .subscribe(res => { - this.startWorkspaceChecker(true); - }); - } + if (this.showWorkspaceDetails) { + this.position = 'relative'; + } else { + this.position = 'fixed'; + } + } + + onUpdateWorkspace(parameters: Array) { + this.state = 'Updating'; + + const body: UpdateWorkspaceBody = { + parameters, + }; + + this.workspaceService.updateWorkspace(this.namespace, this.workspace.uid, body) + .subscribe(res => { + this.startWorkspaceChecker(true); + }); + } }