Skip to content

Commit

Permalink
feat: data offer creation process (#773)
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Treier <[email protected]>
  • Loading branch information
kulgg and richardtreier authored Jul 24, 2024
1 parent 7da1e07 commit 46e2166
Show file tree
Hide file tree
Showing 21 changed files with 373 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Component, Input} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
import {AbstractControl, Validators} from '@angular/forms';

@Component({
selector: 'edit-asset-form-label',
Expand All @@ -8,7 +8,7 @@ import {FormControl, Validators} from '@angular/forms';
export class EditAssetFormLabelComponent {
@Input() label!: string;
@Input() htmlFor?: string;
@Input() ctrl?: FormControl<any>;
@Input() ctrl?: AbstractControl<any>;

isRequired(): boolean {
return this.ctrl?.hasValidator(Validators.required) || false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {MatTooltipModule} from '@angular/material/tooltip';
import {RouterModule} from '@angular/router';
import {CatalogModule} from '../catalog/catalog.module';
import {PipesAndDirectivesModule} from '../pipes-and-directives/pipes-and-directives.module';
import {PolicyEditorModule} from '../policy-editor/policy-editor.module';
import {UiElementsModule} from '../ui-elements/ui-elements.module';
import {DataCategorySelectComponent} from './data-category-select/data-category-select.component';
import {DataSubcategoryItemsPipe} from './data-subcategory-select/data-subcategory-items.pipe';
Expand Down Expand Up @@ -67,6 +68,7 @@ import {TransportModeSelectComponent} from './transport-mode-select/transport-mo
// EDC UI Modules
CatalogModule,
PipesAndDirectivesModule,
PolicyEditorModule,
UiElementsModule,
],
declarations: [
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -475,14 +475,14 @@
type="text"
placeholder="my-asset"
[formControl]="ctrl" />
<mat-error *ngIf="ctrl.invalid && ctrl.errors?.pattern">
<mat-error *ngIf="ctrl.errors?.pattern">
{{ validationMessages.invalidWhitespacesOrColonsMessage }}
</mat-error>
<mat-error *ngIf="ctrl.invalid && ctrl.errors?.requiresPrefix">
<mat-error *ngIf="ctrl.errors?.requiresPrefix">
{{ validationMessages.invalidPrefix('ID', 'urn:artifact') }}
</mat-error>
<mat-error *ngIf="ctrl.invalid && ctrl.errors?.idAlreadyExists">
{{ validationMessages.idExistsErrorMessage }}
<mat-error *ngIf="ctrl.errors?.exists">
{{ ctrl.errors?.exists }}
</mat-error>
</mat-form-field>
</div>
Expand Down Expand Up @@ -926,13 +926,56 @@
</mat-form-field>
</div>
</edit-asset-form-group>

<edit-asset-form-group
*ngIf="form.mode === 'CREATE'"
myTitle="Publishing"
description="Publish data offer to other data space participants">
<!-- Publish Mode -->
<div *ngIf="form.all.controls.publishMode; let ctrl">
<edit-asset-form-label
label="Publishing Mode"
htmlFor="create-asset-form-publish-type"
[ctrl]="ctrl"></edit-asset-form-label>
<mat-radio-group
class="!flex !flex-col !gap-1 w-full mt-2"
aria-label="Select an option"
[formControl]="ctrl">
<mat-radio-button value="PUBLISH_UNRESTRICTED">
Publish unrestricted
</mat-radio-button>
<mat-radio-button value="PUBLISH_RESTRICTED">
Publish restricted
</mat-radio-button>
<mat-radio-button value="DO_NOT_PUBLISH">
Do not publish
</mat-radio-button>
</mat-radio-group>
</div>

<!-- Policy Expression -->
<div *ngIf="form.all.controls.publishMode.value === 'PUBLISH_RESTRICTED'">
<edit-asset-form-label
label="Policy Expression"
[ctrl]="form.all.controls.policyControls"></edit-asset-form-label>
<policy-form-expression
[treeNode]="expressionFormHandler.tree.root"></policy-form-expression>
</div>
</edit-asset-form-group>

<button
class="w-40 h-10 self-end"
mat-raised-button
color="primary"
[disabled]="!form.all.valid || isLoading"
(click)="$event.preventDefault(); submitClicked.emit()">
{{ form.mode === 'CREATE' ? 'Create' : 'Update' }}
{{
form.mode === 'CREATE'
? form.all.controls.publishMode.value === 'DO_NOT_PUBLISH'
? 'Create'
: 'Publish'
: 'Update'
}}
</button>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {ValidationMessages} from 'src/app/core/validators/validation-messages';
import {ExpressionFormHandler} from '../../policy-editor/editor/expression-form-handler';
import {EditAssetForm} from './form/edit-asset-form';
import {DATA_SOURCE_HTTP_METHODS} from './form/http-methods';

@Component({
selector: 'edit-asset-form',
templateUrl: './edit-asset-form.component.html',
providers: [],
})
export class EditAssetFormComponent {
@Input() isLoading!: boolean;
Expand All @@ -17,5 +17,6 @@ export class EditAssetFormComponent {
constructor(
public form: EditAssetForm,
public validationMessages: ValidationMessages,
public expressionFormHandler: ExpressionFormHandler,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {map} from 'rxjs/operators';
import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set';
import {value$} from 'src/app/core/utils/form-group-utils';
import {noWhitespacesOrColonsValidator} from 'src/app/core/validators/no-whitespaces-or-colons-validator';
import {AssetsIdValidatorBuilder} from '../assets-id-validator-builder';
import {EditAssetFormValidators} from './edit-asset-form-validators';
import {AssetEditDialogMode} from './model/asset-edit-dialog-mode';
import {
AssetGeneralFormModel,
Expand All @@ -16,8 +16,8 @@ import {
export class AssetGeneralFormBuilder {
constructor(
private formBuilder: FormBuilder,
private assetsIdValidatorBuilder: AssetsIdValidatorBuilder,
private activeFeatureSet: ActiveFeatureSet,
private editAssetFormValidators: EditAssetFormValidators,
) {}

buildFormGroup(
Expand All @@ -29,7 +29,7 @@ export class AssetGeneralFormBuilder {
id: [
initial.id!,
[Validators.required, noWhitespacesOrColonsValidator],
[this.assetsIdValidatorBuilder.assetIdDoesNotExistsValidator()],
this.editAssetFormValidators.isValidId(),
],
name: [initial.name!, Validators.required],
description: [initial.description!],
Expand Down Expand Up @@ -78,6 +78,8 @@ export class AssetGeneralFormBuilder {
.subscribe(([previousId, currentId]) => {
if (!idCtrl.value || idCtrl.value === previousId) {
idCtrl.setValue(currentId);
idCtrl.markAsTouched();
idCtrl.updateValueAndValidity();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class EditAssetFormInitializer {
forCreate(): EditAssetFormValue {
return {
mode: 'CREATE',
publishMode: 'PUBLISH_UNRESTRICTED',
general: {
id: '',
name: '',
Expand Down Expand Up @@ -50,6 +51,7 @@ export class EditAssetFormInitializer {
forEdit(asset: UiAssetMapped): EditAssetFormValue {
return {
mode: 'EDIT',
publishMode: 'DO_NOT_PUBLISH',
general: {
id: asset.assetId,
name: asset.title,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {Injectable} from '@angular/core';
import {
AbstractControl,
AsyncValidatorFn,
ValidationErrors,
} from '@angular/forms';
import {Observable, combineLatest, of} from 'rxjs';
import {catchError, map} from 'rxjs/operators';
import {IdAvailabilityResponse} from '@sovity.de/edc-client';
import {EdcApiService} from 'src/app/core/services/api/edc-api.service';
import {ALWAYS_TRUE_POLICY_ID} from './model/always-true-policy-id';
import {EditAssetFormValue} from './model/edit-asset-form-model';

/**
* Handles AngularForms for Edit Asset Form
*/
@Injectable({providedIn: 'root'})
export class EditAssetFormValidators {
constructor(private edcApiService: EdcApiService) {}

/**
* Use on asset control, reset asset control on publish mode changes, accesses parent form
*/
isValidId(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
const value = control?.parent?.parent?.value as EditAssetFormValue | null;
if (value?.mode !== 'CREATE') {
return of(null);
}

const assetId = control.value! as string;
if (value.publishMode === 'PUBLISH_UNRESTRICTED') {
return combineLatest([
this.assetIdExistsErrorMessage(assetId),
this.contractDefinitionIdErrorMessage(assetId),
this.policyIdExistsErrorMessage(ALWAYS_TRUE_POLICY_ID).pipe(
map((errorMessage) =>
// We want to throw an error if always-true was not found
errorMessage ? null : 'Always True Policy does not exist.',
),
),
]).pipe(
map((errorMessages) => this.buildValidationErrors(errorMessages)),
);
} else if (value.publishMode === 'PUBLISH_RESTRICTED') {
return combineLatest([
this.assetIdExistsErrorMessage(assetId),
this.contractDefinitionIdErrorMessage(assetId),
this.policyIdExistsErrorMessage(assetId),
]).pipe(
map((errorMessages) => this.buildValidationErrors(errorMessages)),
);
} else {
return this.assetIdExistsErrorMessage(assetId).pipe(
map((result) => this.buildValidationErrors([result])),
);
}

return of(null);
};
}

private assetIdExistsErrorMessage(id: string): Observable<string | null> {
return this.edcApiService.isAssetIdAvailable(id).pipe(
catchError(() => of<IdAvailabilityResponse>({id, available: false})),
map((it) => (it.available ? null : 'Asset already exists.')),
);
}

private contractDefinitionIdErrorMessage(
id: string,
): Observable<string | null> {
return this.edcApiService.isContractDefinitionIdAvailable(id).pipe(
catchError(() => of<IdAvailabilityResponse>({id, available: false})),
map((it) =>
it.available ? null : 'Contract Definition already exists.',
),
);
}

private policyIdExistsErrorMessage(id: string): Observable<string | null> {
return this.edcApiService.isPolicyIdAvailable(id).pipe(
catchError(() => of<IdAvailabilityResponse>({id, available: false})),
map((it) => (it.available ? null : 'Policy already exists.')),
);
}

private buildValidationErrors(
errorMessages: (string | null)[],
): ValidationErrors | null {
const errors = errorMessages.filter((it) => it);
if (!errors.length) {
return null;
}

const message =
errors.length === 3 ? 'Data Offer already exists.' : errors.join(' ');

return {exists: message};
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {delay} from 'rxjs/operators';
import {ExpressionFormControls} from 'src/app/component-library/policy-editor/editor/expression-form-controls';
import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set';
import {switchDisabledControls} from 'src/app/core/utils/form-group-utils';
import {DataCategorySelectItem} from '../../data-category-select/data-category-select-item';
import {AssetAdvancedFormBuilder} from './asset-advanced-form-builder';
import {AssetDatasourceFormBuilder} from './asset-datasource-form-builder';
Expand All @@ -10,6 +13,7 @@ import {AssetDatasourceFormModel} from './model/asset-datasource-form-model';
import {AssetEditDialogMode} from './model/asset-edit-dialog-mode';
import {AssetGeneralFormModel} from './model/asset-general-form-model';
import {DataAddress} from './model/data-address';
import {DataOfferPublishMode} from './model/data-offer-publish-mode';
import {
EditAssetFormModel,
EditAssetFormValue,
Expand Down Expand Up @@ -62,6 +66,7 @@ export class EditAssetForm {
private assetDatasourceFormBuilder: AssetDatasourceFormBuilder,
private assetAdvancedFormBuilder: AssetAdvancedFormBuilder,
private activeFeatureSet: ActiveFeatureSet,
private expressionFormControls: ExpressionFormControls,
) {}

reset(initial: EditAssetFormValue) {
Expand All @@ -84,16 +89,31 @@ export class EditAssetForm {
const formGroup: FormGroup<EditAssetFormModel> =
this.formBuilder.nonNullable.group({
mode: [initial.mode as AssetEditDialogMode],
publishMode: [initial.publishMode as DataOfferPublishMode],
policyControls: this.expressionFormControls.formGroup,
general,
datasource,
});

formGroup.controls.publishMode.valueChanges
.pipe(delay(0))
.subscribe(() => general.controls.id.updateValueAndValidity());

if (this.activeFeatureSet.hasMdsFields()) {
const advanced: FormGroup<AssetAdvancedFormModel> =
this.assetAdvancedFormBuilder.buildFormGroup(initial.advanced!);
formGroup.addControl('advanced', advanced);
}

switchDisabledControls<EditAssetFormValue>(formGroup, (value) => ({
policyControls: value.publishMode === 'PUBLISH_RESTRICTED',
mode: true,
publishMode: true,
advanced: true,
general: true,
datasource: true,
}));

return formGroup;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ALWAYS_TRUE_POLICY_ID = 'always-true';
Loading

0 comments on commit 46e2166

Please sign in to comment.