Skip to content

Commit 7a22ca7

Browse files
committed
allow upload of schema as a file and create a default schema
1 parent 84b3f46 commit 7a22ca7

25 files changed

+859
-65
lines changed

src/app/ingestor/ingestor-dialogs/creation-dialog/creation-pages/ingestor.new-transfer-dialog-page.component.ts

Lines changed: 113 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import * as fromActions from "state-management/actions/ingestor.actions";
3535
import { selectUserSettingsPageViewModel } from "state-management/selectors/user.selectors";
3636
import { Subscription } from "rxjs";
3737
import { ReturnedUserDto } from "@scicatproject/scicat-sdk-ts-angular";
38+
import * as defaultMetadataSchema from "shared/modules/scientific-metadata/defaultMetadata.json";
3839

3940
@Component({
4041
selector: "ingestor-new-transfer-dialog-page",
@@ -85,6 +86,7 @@ export class IngestorNewTransferDialogPageComponent
8586
isCardContentVisible = {
8687
scicat: true,
8788
};
89+
experimentalBannerDismissed = false;
8890

8991
constructor(
9092
private store: Store,
@@ -172,6 +174,10 @@ export class IngestorNewTransferDialogPageComponent
172174
}
173175
}
174176

177+
dismissExperimentalBanner(): void {
178+
this.experimentalBannerDismissed = true;
179+
}
180+
175181
ngOnDestroy(): void {
176182
this.subscriptions.forEach((subscription) => {
177183
subscription.unsubscribe();
@@ -334,7 +340,10 @@ export class IngestorNewTransferDialogPageComponent
334340
}
335341

336342
onSchemaUrlChange(url: string): void {
343+
// Store the URL
337344
this.createNewTransferData.schemaUrl = url;
345+
346+
// Update the store
338347
this.store.dispatch(
339348
fromActions.updateIngestionObject({
340349
ingestionObject: this.createNewTransferData,
@@ -343,44 +352,127 @@ export class IngestorNewTransferDialogPageComponent
343352
}
344353

345354
async onUploadSchema(): Promise<void> {
346-
if (!this.createNewTransferData.schemaUrl) {
347-
alert("Please enter a schema URL.");
355+
// Don't validate if URL is empty (user may have just cleared it)
356+
if (
357+
!this.createNewTransferData.schemaUrl ||
358+
this.createNewTransferData.schemaUrl.trim() === ""
359+
) {
348360
return;
349361
}
362+
350363
console.log(
351364
"Fetching schema from URL:",
352365
this.createNewTransferData.schemaUrl,
353366
);
354-
let content: string;
355-
let parsedJson: any;
356367

357368
try {
369+
// Fetch the schema from the URL
358370
const response = await fetch(this.createNewTransferData.schemaUrl);
359371
if (!response.ok) {
360372
throw new Error(`Failed to fetch schema: ${response.statusText}`);
361373
}
362-
content = await response.text();
363-
try {
364-
parsedJson = JSON.parse(content);
365-
} catch (e) {
374+
375+
const content = await response.text();
376+
377+
// Validate that it's valid JSON
378+
JSON.parse(content);
379+
380+
// Store the schema content
381+
this.createNewTransferData.selectedSchemaFileContent = content;
382+
383+
// Update the store
384+
this.store.dispatch(
385+
fromActions.updateIngestionObject({
386+
ingestionObject: this.createNewTransferData,
387+
}),
388+
);
389+
390+
this.validateNextButton();
391+
} catch (error: any) {
392+
// Clear both URL and content on error
393+
this.createNewTransferData.schemaUrl = "";
394+
this.createNewTransferData.selectedSchemaFileContent = "";
395+
396+
// Update the store with cleared values
397+
this.store.dispatch(
398+
fromActions.updateIngestionObject({
399+
ingestionObject: this.createNewTransferData,
400+
}),
401+
);
402+
403+
// Show appropriate error message
404+
if (error instanceof SyntaxError) {
366405
alert("The provided URL does not contain valid JSON.");
367-
return;
406+
} else {
407+
alert(`Error fetching schema: ${error.message}.`);
368408
}
369-
} catch (error: any) {
370-
alert(`Error fetching schema: ${error.message}`);
371-
return;
409+
console.error("Error loading schema from URL:", error);
410+
411+
this.validateNextButton();
372412
}
413+
}
373414

374-
this.createNewTransferData.selectedSchemaFileContent = content;
415+
onUploadSchemaFile(): void {
416+
const input = document.createElement("input");
417+
input.type = "file";
418+
input.accept = ".json,application/json";
419+
input.onchange = (event: Event) => {
420+
const target = event.target as HTMLInputElement;
421+
const file = target.files?.[0];
422+
if (file) {
423+
const reader = new FileReader();
424+
reader.onload = (e) => {
425+
const content = e.target?.result as string;
375426

376-
// Update the store with both the URL and content
377-
this.createNewTransferData.selectedSchemaFileContent = content;
378-
this.store.dispatch(
379-
fromActions.updateIngestionObject({
380-
ingestionObject: this.createNewTransferData,
381-
}),
382-
);
427+
try {
428+
// Validate that it's valid JSON
429+
JSON.parse(content);
383430

384-
this.validateNextButton();
431+
this.createNewTransferData.selectedSchemaFileContent = content;
432+
433+
// Clear the URL since we're loading from file
434+
this.createNewTransferData.schemaUrl = "";
435+
436+
// Update the store
437+
this.store.dispatch(
438+
fromActions.updateIngestionObject({
439+
ingestionObject: this.createNewTransferData,
440+
}),
441+
);
442+
443+
this.validateNextButton();
444+
} catch (error) {
445+
alert("The selected file does not contain valid JSON.");
446+
console.error("Error parsing JSON file:", error);
447+
}
448+
};
449+
reader.readAsText(file);
450+
}
451+
};
452+
input.click();
453+
}
454+
455+
onLoadDefaultSchema(): void {
456+
try {
457+
// Convert the imported schema to a string
458+
const content = JSON.stringify(defaultMetadataSchema, null, 2);
459+
460+
// Store the schema content
461+
this.createNewTransferData.selectedSchemaFileContent = content;
462+
463+
// Clear the URL since we're loading from default schema
464+
this.createNewTransferData.schemaUrl = "";
465+
466+
this.store.dispatch(
467+
fromActions.updateIngestionObject({
468+
ingestionObject: this.createNewTransferData,
469+
}),
470+
);
471+
472+
this.validateNextButton();
473+
} catch (error) {
474+
alert("Error loading default schema.");
475+
console.error("Error loading default schema:", error);
476+
}
385477
}
386478
}

src/app/ingestor/ingestor-dialogs/creation-dialog/creation-pages/ingestor.new-transfer-dialog-page.html

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1-
<div class="ingestor-dialog-header">
1+
2+
<!-- Experimental Feature Warning Banner -->
3+
<div
4+
*ngIf="createNewTransferData.editorMode === 'CREATION' && !experimentalBannerDismissed"
5+
class="experimental-warning-banner"
6+
>
7+
<mat-icon class="warning-icon">warning</mat-icon>
8+
<span class="warning-text">
9+
<strong>Experimental Feature:</strong> This feature is experimental. Use at your own risk.
10+
</span>
11+
<button
12+
mat-icon-button
13+
class="close-btn"
14+
(click)="dismissExperimentalBanner()"
15+
aria-label="Dismiss warning"
16+
>
17+
<mat-icon>close</mat-icon>
18+
</button>
19+
</div>
20+
<div class="ingestor-dialog-header">
221
<div class="ingestor-mixed-header">
322
<h2
423
*ngIf="createNewTransferData.editorMode === 'CREATION'"
@@ -69,32 +88,82 @@
6988
<mat-card-title>Scientific Metadata Schema</mat-card-title>
7089
</mat-card-header>
7190
<mat-card-content class="ingestor-vertical-layout">
91+
<p>
92+
You can provide a schema for your scientific metadata. We only
93+
accept schemas in JSON format.
94+
</p>
95+
96+
<!-- Option 1: Load from URL -->
7297
<div class="ingestor-upload-section">
73-
<label for="schemaUrl">
74-
You can provide a schema for your scientific metadata. We only
75-
accept schemas in json format.
76-
</label>
98+
<div class="schema-option-header">
99+
<mat-icon class="option-icon">link</mat-icon>
100+
<h4>Load from URL</h4>
101+
</div>
102+
<p class="option-description">
103+
Provide a URL to a JSON schema file. The schema will be validated automatically when you finish typing.
104+
</p>
77105
<mat-form-field class="form-full-width">
106+
<mat-label>Schema URL</mat-label>
78107
<input
79108
matInput
80109
id="schemaUrl"
81-
placeholder="Paste schema URL here"
110+
placeholder="https://example.com/schema.json"
82111
[ngModel]="createNewTransferData.schemaUrl"
83112
(ngModelChange)="onSchemaUrlChange($event)"
113+
(blur)="onUploadSchema()"
84114
/>
85-
<button
86-
mat-raised-button
87-
class="block-button"
88-
color="primary"
89-
(click)="onUploadSchema()"
90-
>
91-
Validate
92-
</button>
115+
<mat-icon matPrefix>language</mat-icon>
93116
</mat-form-field>
94-
<p *ngIf="this.createNewTransferData.selectedSchemaFileContent">
95-
Loaded schema is valid!
117+
</div>
118+
119+
<mat-divider></mat-divider>
120+
121+
<!-- Option 2: Upload from file -->
122+
<div class="ingestor-upload-section">
123+
<div class="schema-option-header">
124+
<mat-icon class="option-icon">upload_file</mat-icon>
125+
<h4>Upload from File</h4>
126+
</div>
127+
<p class="option-description">
128+
Select a JSON schema file from your local computer.
129+
</p>
130+
<button
131+
mat-raised-button
132+
color="primary"
133+
(click)="onUploadSchemaFile()"
134+
class="schema-action-button"
135+
>
136+
<mat-icon>folder_open</mat-icon>
137+
Choose File
138+
</button>
139+
</div>
140+
141+
<mat-divider></mat-divider>
142+
143+
<!-- Option 3: Use default schema -->
144+
<div class="ingestor-upload-section">
145+
<div class="schema-option-header">
146+
<mat-icon class="option-icon">file_present</mat-icon>
147+
<h4>Use Default Schema</h4>
148+
</div>
149+
<p class="option-description">
150+
Load the pre-configured default metadata schema for standard datasets.
96151
</p>
152+
<button
153+
mat-raised-button
154+
color="primary"
155+
(click)="onLoadDefaultSchema()"
156+
class="schema-action-button"
157+
>
158+
<mat-icon>check_circle</mat-icon>
159+
Load Default
160+
</button>
97161
</div>
162+
163+
<!-- Status message -->
164+
<p *ngIf="this.createNewTransferData.selectedSchemaFileContent" class="schema-status-success">
165+
Schema loaded successfully!
166+
</p>
98167
</mat-card-content>
99168
</mat-card>
100169
</div>

src/app/ingestor/ingestor-dialogs/dialog-mounting-components/ingestor.dialog-stepper.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,15 @@
159159
[editable]="false"
160160
[stepControl]="null"
161161
>
162-
<ng-template matStepLabel>Fill out Dataset information</ng-template>
162+
<ng-template matStepLabel>Dataset information</ng-template>
163163
</mat-step>
164164

165165
<mat-step
166166
[completed]="activeStep > 3"
167167
[editable]="false"
168168
[stepControl]="null"
169169
>
170-
<ng-template matStepLabel>Confirm inputs</ng-template>
170+
<ng-template matStepLabel>Confirm</ng-template>
171171
</mat-step>
172172
</mat-stepper>
173173
</div>

src/app/ingestor/ingestor-metadata-editor/ingestor-metadata-editor-helper.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import { angularMaterialRenderers } from "@jsonforms/angular-material";
2-
import { customRenderers } from "./customRenderer/custom-renderers";
31
import { JsonSchema } from "@jsonforms/core";
2+
import { configuredRenderer } from "shared/modules/jsonforms-custom-renderers/ingestor-renderer/ingestor-renderer-helper";
43

5-
export const configuredRenderer = [
6-
...customRenderers,
7-
...angularMaterialRenderers,
8-
];
4+
export { configuredRenderer };
95

106
export class IngestorMetadataEditorHelper {
117
// Resolve all $ref in a schema

src/app/ingestor/ingestor-page/ingestor-creation.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export class IngestorCreationComponent {
3333
dialogRef = this.dialog.open(IngestorCreationDialogBaseComponent, {
3434
disableClose: true,
3535
width: "auto",
36+
minWidth: "40%",
3637
maxWidth: "1400px",
3738
});
3839

src/app/ingestor/ingestor-page/ingestor-transfer.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ export class IngestorTransferComponent implements OnInit, OnDestroy {
282282
dialogRef = this.dialog.open(IngestorCreationDialogBaseComponent, {
283283
disableClose: true,
284284
width: "auto",
285+
minWidth: "60%",
285286
maxWidth: "1400px",
286287
});
287288

0 commit comments

Comments
 (0)