Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[stepIndex]="1" altConfirm="Continue witout a configuraiton"
[invalid]="configFileCache === null" (confirm)="uploadCachedConfiguration()">
Upload an existing configuration:
<app-file-upload accept=".yml,.yaml" [disabled]="data.locked || !oneEnabled"
<app-file-upload #standardFileUpload accept=".yml,.yaml" [disabled]="data.locked || !oneEnabled"
(input)="cacheConfiguration($event)"></app-file-upload>
</app-workstep-item>

Expand Down Expand Up @@ -70,7 +70,7 @@

<app-workstep-box header="Configuration Upload">
Upload an existing configuration:
<app-file-upload accept=".yml,.yaml" [disabled]="data.locked || !oneEnabled"
<app-file-upload #expertFileUpload accept=".yml,.yaml" [disabled]="data.locked || !oneEnabled"
(input)="uploadConfiguration($event)"></app-file-upload>
</app-workstep-box>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Mode } from "@core/enums/mode";
import { Steps } from "@core/enums/steps";
import { AppNotification, NotificationService } from "@core/services/notification.service";
import { StateManagementService } from "@core/services/state-management.service";
import { FileUploadComponent } from "@shared/components/file-upload/file-upload.component";
import { DataConfiguration } from "@shared/model/data-configuration";
import { Status } from "@shared/model/status";
import { DataConfigurationService } from "@shared/services/data-configuration.service";
Expand Down Expand Up @@ -89,6 +90,8 @@ export class ConfigurationPageComponent implements OnInit {

@ViewChild('selection') private selection: ConfigurationSelectionComponent;
@ViewChild('form') protected forms: ConfigurationFormComponent;
@ViewChild('expertFileUpload') protected expertFileUpload: FileUploadComponent;
@ViewChild('standardFileUpload') protected standardFileUpload: FileUploadComponent;

constructor(
protected readonly algorithmService: AlgorithmService,
Expand Down Expand Up @@ -289,6 +292,7 @@ export class ConfigurationPageComponent implements OnInit {
this.configurationService.uploadAllConfigurations(this.configFileCache, included).subscribe({
next: () => {
this.configFileCache = null;
this.standardFileUpload.clearFile();
},
error: error => {
this.errorHandlingService.addError(error, "Could not upload configuration.");
Expand All @@ -311,6 +315,7 @@ export class ConfigurationPageComponent implements OnInit {
this.configurationService.uploadAllConfigurations(file, included).subscribe({
next: () => {
this.notificationService.addNotification(new AppNotification("Successfully imported the configuration.", "success"));
this.expertFileUpload.clearFile();
},
error: error => {
this.errorHandlingService.addError(error, "Could not upload configuration.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<input hidden (input)="onFileInput($event)" #fileInput type="file" [accept]="accept" [disabled]="disabled">
<span *ngIf="dataFile === null" [class.drop-text-disabled]="disabled">Choose file or drag and drop</span>
<span *ngIf="dataFile !== null" [class.drop-text-disabled]="disabled">{{ dataFile.name }}</span>
<div *ngIf="dataFile !== null" class="drop-zone-delete icon-enabled" (click)="clearFile();">
<mat-icon>delete</mat-icon>
</div>
</div>
</div>
<div class="cinnamon-error" *ngIf="errors.large">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import "../../../styles/colors.less";
@import "../../../styles/layout.less";
@import "../../../styles/styling.less";

.drop-zone {
border: 2px dashed @color-border;
Expand All @@ -22,6 +23,10 @@
gap: 10px;
}

.drop-zone-delete {
margin-left: auto;
}

.drop-text-disabled {
color: @color-unimportant;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export class FileUploadComponent implements OnInit {
this.appConfig$ = this.appConfigService.appConfig$;
}

/**
* Clears the file input.
*/
public clearFile(): void {
this.dataFile = null;
}

/**
* Clears the previous value and opens the file select dialog.
* @protected
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConfigurationImportSummary } from "@shared/model/import-pipe-data";
import { Type } from "class-transformer";

/**
Expand All @@ -18,5 +19,6 @@ export class ErrorResponse {

export class ErrorDetails {
configurationName: string | null;
configurationImportSummary?: ConfigurationImportSummary;
validationErrors: Record<string, string[]> | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export abstract class AlgorithmService {
return this.fetchInfo().pipe(
switchMap(value => {
// Either all processes are configured or none, so look up if the first is configured
if (value.processes[0].configured) {
if (value.available) {
// Ensure algorithms are loaded before fetching the configuration
return this.algorithms.pipe(
switchMap(_ => {
Expand Down Expand Up @@ -256,6 +256,10 @@ export abstract class AlgorithmService {
* Information for the configuration page.
*/
export interface ConfigurationInfo {
/**
* If a configuration is available because of a previous import.
*/
available: boolean
/**
* Processes to be configured by this configuration page.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ export class ConfigurationService {
public uploadAllConfigurations(file: Blob, includedConfigurations: Array<string> | null): Observable<ConfigurationImportSummary> {
const formData = new FormData();
formData.append("configuration", file);
formData.append("importParameters", JSON.stringify({configurationsToImport: includedConfigurations}));
formData.append("importParameters", JSON.stringify({
configurationsToImport: includedConfigurations,
allowPartialImport: false,
}));

// TODO currently there is always just one configuration imported.
// If multiple configurations are uploaded, handling for PARTIAL_ERROR must be implemented.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HttpErrorResponse } from "@angular/common/http";
import { Injectable } from '@angular/core';
import { AppNotification, NotificationService } from "@core/services/notification.service";
import { HttpErrorResponse } from "@angular/common/http";
import { ErrorDetails, ErrorResponse } from "../model/error-response";
import { ConfigurationImportSummary } from "@shared/model/import-pipe-data";
import { plainToInstance } from "class-transformer";
import { ErrorDetails, ErrorResponse } from "../model/error-response";
import { UserService } from "./user.service";

/**
Expand Down Expand Up @@ -75,7 +76,7 @@ export class ErrorHandlingService {
if (this.isJsonString(response.error)) {
const errorResponse = plainToInstance(ErrorResponse, JSON.parse(response.error));
return this.handleErrorResponse(errorResponse);
} else if(typeof response.error === 'object') {
} else if (typeof response.error === 'object') {
const errorResponse = plainToInstance(ErrorResponse, response.error);
return this.handleErrorResponse(errorResponse);
} else {
Expand All @@ -86,7 +87,18 @@ export class ErrorHandlingService {
private handleErrorResponse(error: ErrorResponse): string {
let errorMessage = "";

if (error.errorCode === 'PLATFORM_3_2_1') {
if (error.errorCode === "PLATFORM_1_14_3") {
const summary = error.errorDetails?.configurationImportSummary as ConfigurationImportSummary;
const target = summary.parameters.configurationsToImport![0];
const part = summary.configurationImportSummaries.find(p => p.configurationName === target);

if (part != null) {
if (part.errorCode === "PLATFORM_1_2_2") {
return `Failed to import configuration: The file does not contain a configuration for "${part.configurationName}"!`;
}
}

} else if (error.errorCode === 'PLATFORM_3_2_1') {
if (error.errorDetails?.validationErrors == null) {
console.error("Validation error details are null!");
return "Request validation failed.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
@Schema(description = "General information about a configuration.")
@Getter @AllArgsConstructor
public class ConfigurationInfo {

/**
* If a configuration is available because of a previous import.
*/
@Schema(description = "If a configuration is available because of a previous import.")
private final boolean available;

@Schema(description = "Information about all processes that use this configuration.")
private final List<ProcessInfo> processes = new ArrayList<>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashSet;

/**
* Service class for accessing and managing configurations.
Expand Down Expand Up @@ -97,12 +98,17 @@ private ConfigurationImportSummary importConfigurations(

final var importSummary = new ConfigurationImportSummary(parameters);

final var seenConfigs = new HashSet<String>();

// Iterate over all configurations in the file and import those marked for import
final var configItr = yamlConfig.fields();

while (configItr.hasNext()) {
final var configEntry = configItr.next();
final var configName = configEntry.getKey();

seenConfigs.add(configName);

if (parameters.getConfigurationsToImport() != null &&
!parameters.getConfigurationsToImport().contains(configName)) {
importSummary.addIgnored(configName);
Expand Down Expand Up @@ -180,6 +186,18 @@ private ConfigurationImportSummary importConfigurations(
}
}

// Check if all configurations to import were in the file
if (parameters.getConfigurationsToImport() != null) {
for (final var configToImport : parameters.getConfigurationsToImport()) {
if (!seenConfigs.contains(configToImport)) {
importSummary.addError(configToImport, new BadConfigurationNameException(
BadConfigurationNameException.NO_CONFIGURATION,
"The file does not contain the configuration " + configToImport).getErrorCode());
}
}
}

// Check if the import failed according to the partial import
if (importSummary.getStatus() == ConfigurationImportSummary.ConfigurationImportStatus.ERROR) {
// Throw exception to trigger rollback
throw new BadConfigurationFileException(BadConfigurationFileException.IMPORT_FAILED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,13 @@ public void clearCaches() {
*/
public ConfigurationInfo getInfo(final String configurationName, final ProjectEntity project)
throws BadConfigurationNameException, InternalInvalidStateException {
final ConfigurationInfo configurationInfo = new ConfigurationInfo();

final ExternalConfiguration externalConfiguration = stepService.getExternalConfiguration(configurationName);

final var configurationList = project.getConfigurationList(externalConfiguration);
final var isAvailable = configurationList != null && !configurationList.getConfigurations().isEmpty();
final ConfigurationInfo configurationInfo = new ConfigurationInfo(isAvailable);

final var jobs = externalConfiguration.getUsages()
.stream()
.map(ExternalEndpoint::getUsages)
Expand Down
Loading