-
Notifications
You must be signed in to change notification settings - Fork 34
feat: published data refactor #1896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…uirements and workflow
…to published-data-refactor
…to published-data-refactor
…to published-data-refactor
…to published-data-refactor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @martin-trajanovski - I've reviewed your changes - here's some feedback:
- Consider splitting the large PublishedDataEffects class into smaller, feature-focused effect files (e.g. CRUD effects, localStorage effects, batch-related effects) to improve readability and maintainability.
- The unsaved-changes and beforeUnload logic is duplicated in PublishComponent and PublisheddataEditComponent—extracting it into a reusable guard or base class would reduce boilerplate and keep components focused.
- New flows like saveDataPublicationInLocalStorage, clearDataPublicationFromLocalStorage, storeEditingPublishedDataDoi and fetchRelatedDatasetsAndAddToBatch lack dedicated unit tests—please add tests to cover these critical behaviors.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider splitting the large PublishedDataEffects class into smaller, feature-focused effect files (e.g. CRUD effects, localStorage effects, batch-related effects) to improve readability and maintainability.
- The unsaved-changes and beforeUnload logic is duplicated in PublishComponent and PublisheddataEditComponent—extracting it into a reusable guard or base class would reduce boilerplate and keep components focused.
- New flows like saveDataPublicationInLocalStorage, clearDataPublicationFromLocalStorage, storeEditingPublishedDataDoi and fetchRelatedDatasetsAndAddToBatch lack dedicated unit tests—please add tests to cover these critical behaviors.
## Individual Comments
### Comment 1
<location> `src/app/publisheddata/publisheddata-edit/publisheddata-edit.component.ts:167` </location>
<code_context>
+ },
+ );
+
+ this.form.valueChanges.subscribe(() => {
+ if (this.form.dirty) {
+ this._hasUnsavedChanges = true;
</code_context>
<issue_to_address>
Consider unsubscribing from form.valueChanges on destroy.
Store the subscription and unsubscribe in ngOnDestroy to prevent potential memory leaks and maintain consistency.
Suggested implementation:
```typescript
+ this.formValueChangesSubscription = this.form.valueChanges.subscribe(() => {
+ if (this.form.dirty) {
+ this._hasUnsavedChanges = true;
+ }
+ });
```
```typescript
}
ngOnDestroy() {
if (this.formValueChangesSubscription) {
this.formValueChangesSubscription.unsubscribe();
}
// (If you already have other cleanup logic in ngOnDestroy, merge this in.)
}
```
- Add a class property: `private formValueChangesSubscription: Subscription;` at the top of your component class (with your other properties).
- Make sure to import `Subscription` from `rxjs` if it's not already imported:
`import { Subscription } from 'rxjs';`
</issue_to_address>
### Comment 2
<location> `src/app/publisheddata/publisheddata-details/publisheddata-details.component.html:102` </location>
<code_context>
- <tr *ngIf="publishedData.creator as value">
- <th>Creator</th>
- <td>{{ value }}</td>
+ <tr *ngIf="publishedData.metadata?.creators as creators">
+ <th>Creators</th>
+
</code_context>
<issue_to_address>
Rendering of creators and contributors assumes array of objects with 'name'.
If the schema changes and 'creators' or 'contributors' are not arrays of objects with a 'name' property, this may cause runtime errors. Please add checks or fallback rendering to handle such cases.
</issue_to_address>
### Comment 3
<location> `src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts:68` </location>
<code_context>
}
onRegisterClick(doi: string) {
- this.store.dispatch(registerPublishedDataAction({ doi }));
+ if (
</code_context>
<issue_to_address>
The confirmation dialog for registration is a blocking browser confirm.
Switching to a custom modal will improve consistency and allow for more flexibility in presenting information or actions.
Suggested implementation:
```typescript
show = false;
showRegisterConfirmModal = false;
pendingRegisterDoi: string | null = null;
}
onRegisterClick(doi: string) {
this.pendingRegisterDoi = doi;
this.showRegisterConfirmModal = true;
}
onRegisterConfirm() {
if (this.pendingRegisterDoi) {
this.store.dispatch(registerPublishedDataAction({ doi: this.pendingRegisterDoi }));
this.pendingRegisterDoi = null;
}
this.showRegisterConfirmModal = false;
}
onRegisterCancel() {
this.pendingRegisterDoi = null;
this.showRegisterConfirmModal = false;
}
```
You will also need to:
1. Add the custom modal component to the template (e.g., using Angular Material's `<mat-dialog>` or your own modal implementation).
2. Bind `[open]="showRegisterConfirmModal"` (or equivalent) and wire up the modal's confirm/cancel actions to `onRegisterConfirm()` and `onRegisterCancel()`.
3. Optionally, create a reusable confirmation modal component if one does not already exist in your codebase.
</issue_to_address>
### Comment 4
<location> `src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts:97` </location>
<code_context>
this.router.navigateByUrl("/publishedDatasets/" + id + "/edit");
}
+ onEditDatasetList() {
+ this.store.dispatch(
+ fetchRelatedDatasetsAndAddToBatchAction({
</code_context>
<issue_to_address>
No check for datasetPids existence before dispatching fetchRelatedDatasetsAndAddToBatchAction.
Add a guard clause to ensure 'datasetPids' is defined and not empty before dispatching the action.
</issue_to_address>
### Comment 5
<location> `src/app/state-management/effects/published-data.effects.ts:335` </location>
<code_context>
+ switchMap((errors) => {
+ const message = {
+ type: MessageType.Error,
+ content: `Registration Failed. ${errors.error.map((e) => e.replaceAll("instance", "metadata")).join(", ")}`,
+ duration: 5000,
+ };
</code_context>
<issue_to_address>
Assumes 'errors.error' is always an array.
Add a type check or fallback to handle cases where 'errors.error' is not an array to prevent runtime errors.
</issue_to_address>
### Comment 6
<location> `src/app/state-management/effects/published-data.effects.ts:349` </location>
<code_context>
const message = {
type: MessageType.Error,
- content: "Registration Failed",
+ content: `Publishing Failed. ${errors.error.map((e) => e.replaceAll("instance", "metadata")).join(", ")}`,
duration: 5000,
};
</code_context>
<issue_to_address>
Same array assumption for publishPublishedDataFailedMessage$ as above.
Ensure 'errors.error' is always an array to prevent runtime errors when calling 'map'.
</issue_to_address>
### Comment 7
<location> `src/app/datasets/publish/publish.component.ts:82` </location>
<code_context>
- if (event.input) {
- event.input.value = "";
+ public formIsValid() {
+ if (!Object.values(this.form).includes(undefined)) {
+ return this.form.title.length > 0 && this.form.abstract.length > 0;
+ } else {
</code_context>
<issue_to_address>
formIsValid() only checks for undefined, not for empty arrays.
Also ensure that required arrays like 'datasetPids' are not empty when validating the form.
</issue_to_address>
### Comment 8
<location> `src/app/datasets/publish/publish.component.ts:124` </location>
<code_context>
)
.subscribe();
+ this.publishedDataConfigSubscription = this.publishedDataConfig$.subscribe(
+ (publishedDataConfig) => {
+ if (!isEmpty(publishedDataConfig)) {
</code_context>
<issue_to_address>
No check for schema.required existence before splice.
If publishedDataConfig.metadataSchema.required is undefined, calling splice will throw an error. Please add a check to ensure it exists before using splice.
</issue_to_address>
### Comment 9
<location> `src/app/publisheddata/publisheddata-edit/publisheddata-edit.component.ts:132` </location>
<code_context>
)
.subscribe();
+ this.publishedDataConfigSubscription = this.publishedDataConfig$.subscribe(
+ (publishedDataConfig) => {
+ if (!isEmpty(publishedDataConfig)) {
</code_context>
<issue_to_address>
No check for schema.required existence before splice.
Please add a check to confirm 'required' exists before using splice to prevent runtime errors.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/app/publisheddata/publisheddata-edit/publisheddata-edit.component.ts
Outdated
Show resolved
Hide resolved
src/app/publisheddata/publisheddata-details/publisheddata-details.component.html
Outdated
Show resolved
Hide resolved
src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts
Show resolved
Hide resolved
src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts
Show resolved
Hide resolved
src/app/publisheddata/publisheddata-edit/publisheddata-edit.component.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First review with few comments.
I'm still looking through the code and testing locally on my machine
@@ -21,6 +22,7 @@ const routes: Routes = [ | |||
path: "batch/publish", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if we should change the routing to be closer to the published data
JsonFormsModule, | ||
JsonFormsAngularMaterialModule, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we importing the json forms in the datasets module?
expect({ ...action }).toEqual({ | ||
type: "[PublishedData] Publish Dataset", | ||
type: "[PublishedData] Create Data Publication", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think should read Create Published Data Record
and not Create Data Publication
export const createDataPublicationAction = createAction( | ||
"[PublishedData] Create Data Publication", | ||
props<{ data: CreatePublishedDataDto }>(), | ||
); | ||
export const publishDatasetCompleteAction = createAction( | ||
"[PublishedData] Publish Dataset Complete", | ||
export const createDataPublicationCompleteAction = createAction( | ||
"[PublishedData] Create Data Publication Complete", | ||
props<{ publishedData: PublishedData }>(), | ||
); | ||
export const createDataPublicationFailedAction = createAction( | ||
"[PublishedData] Create Data Publication Failed", | ||
); | ||
export const saveDataPublicationAction = createAction( | ||
"[PublishedData] Save Data Publication", | ||
props<{ data: CreatePublishedDataDto }>(), | ||
); | ||
export const saveDataPublicationCompleteAction = createAction( | ||
"[PublishedData] Save Data Publication Complete", | ||
props<{ publishedData: PublishedData }>(), | ||
); | ||
export const saveDataPublicationFailedAction = createAction( | ||
"[PublishedData] Save Data Publication Failed", | ||
); | ||
export const saveDataPublicationInLocalStorage = createAction( | ||
"[PublishedData] Save Data Publication In Local Storage", | ||
props<{ publishedData: PublishedData }>(), | ||
); | ||
export const publishDatasetFailedAction = createAction( | ||
"[PublishedData] Publish Dataset Failed", | ||
export const clearDataPublicationFromLocalStorage = createAction( | ||
"[PublishedData] Clear Data Publication In Local Storage", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we call it Data Publication?
@@ -103,24 +125,81 @@ export class PublishedDataEffects { | |||
{ dispatch: false }, | |||
); | |||
|
|||
publishDataset$ = createEffect(() => { | |||
saveDataPublicationInLocalStorage$ = createEffect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about the naming DataPublication
. Is this different than PublishedData
?
Description
This PR aims to refactor the published data following the new workflow shown in the picture bellow.
Motivation
The old published data workflow was outdated and wasn't using all the metadata fields that can be provided to DataCite
Fixes:
Please provide a list of the fixes implemented in this PR
Changes:
Please provide a list of the changes implemented by this PR
Tests included
Documentation
official documentation info
If you have updated the official documentation, please provide PR # and URL of the pages where the updates are included
Backend version
Summary by Sourcery
Refactor the published data workflow to support dynamic metadata configuration, full CRUD operations, and improved user experience.
New Features:
Enhancements:
Build:
Tests:
Chores: