Skip to content

Commit

Permalink
ufal/fe-show-checksum-result (#432)
Browse files Browse the repository at this point in the history
* BitstreamChecksum values are fetched and parsed from the BE

* Checksum info is showed up.

* Added messages and translations.

* Added docs and refactored code

* Fixed failing tests

* Fixed wrong czech translations.
  • Loading branch information
milanmajchrak authored Jan 3, 2024
1 parent 0484c9d commit 6be9911
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 10 deletions.
36 changes: 36 additions & 0 deletions src/app/core/bitstream-checksum-data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import { dataService } from './data/base/data-service.decorator';
import { BaseDataService } from './data/base/base-data.service';
import { RequestService } from './data/request.service';
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { CoreState } from './core-state.model';
import { HALEndpointService } from './shared/hal-endpoint.service';
import { ObjectCacheService } from './cache/object-cache.service';
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service';
import { HttpClient } from '@angular/common/http';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { linkName } from './data/clarin/clrua-data.service';
import { BitstreamChecksum } from './shared/bitstream-checksum.model';

/**
* A service responsible for fetching BitstreamChecksum objects from the REST API
*/
@Injectable()
@dataService(BitstreamChecksum.type)
export class BitstreamChecksumDataService extends BaseDataService<BitstreamChecksum> {
protected linkPath = 'checksum';

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService,
protected comparator: DefaultChangeAnalyzer<BitstreamChecksum>,
protected http: HttpClient,
protected notificationsService: NotificationsService,
) {
super(linkName, requestService, rdbService, objectCache, halService, undefined);
}
}
6 changes: 5 additions & 1 deletion src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ import { ClarinUserMetadataDataService } from './data/clarin/clarin-user-metadat
import { ClarinLicenseResourceMappingService } from './data/clarin/clarin-license-resource-mapping-data.service';
import { ClarinVerificationTokenDataService } from './data/clarin/clarin-verification-token-data.service';
import { ClruaDataService } from './data/clarin/clrua-data.service';
import { BitstreamChecksum } from './shared/bitstream-checksum.model';
import { BitstreamChecksumDataService } from './bitstream-checksum-data.service';

/**
* When not in production, endpoint responses can be mocked for testing purposes
Expand Down Expand Up @@ -322,7 +324,8 @@ const PROVIDERS = [
OrcidQueueDataService,
OrcidHistoryDataService,
SupervisionOrderDataService,
HandleDataService
HandleDataService,
BitstreamChecksumDataService
];

/**
Expand All @@ -335,6 +338,7 @@ export const models =
Bundle,
Bitstream,
BitstreamFormat,
BitstreamChecksum,
Item,
Site,
Collection,
Expand Down
63 changes: 63 additions & 0 deletions src/app/core/shared/bitstream-checksum.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { BITSTREAM_CHECKSUM } from './bitstream-checksum.resource';
import { excludeFromEquals } from '../utilities/equals.decorators';
import { autoserialize, deserialize } from 'cerialize';
import { ResourceType } from './resource-type';
import { HALLink } from './hal-link.model';
import { typedObject } from '../cache/builders/build-decorators';
import { TypedObject } from '../cache/typed-object.model';


/**
* Model class containing the checksums of a bitstream (local, S3, DB)
*/
@typedObject
export class BitstreamChecksum extends TypedObject {
/**
* The `bitstreamchecksum` object type.
*/
static type = BITSTREAM_CHECKSUM;

/**
* The object type
*/
@excludeFromEquals
@autoserialize
type: ResourceType;

/**
* The identifier of this BitstreamChecksum object
*/
@autoserialize
id: string;

/**
* The checksum of the active store (local/S3)
*/
@autoserialize
activeStore: CheckSum;

/**
* The checksum from the database
*/
@autoserialize
databaseChecksum: CheckSum;

/**
* The checksum of the synchronized store (S3, local)
*/
@autoserialize
synchronizedStore: CheckSum;

@deserialize
_links: {
self: HALLink
};
}

/**
* Model class containing a checksum value and algorithm
*/
export interface CheckSum {
checkSumAlgorithm: string;
value: string;
}
9 changes: 9 additions & 0 deletions src/app/core/shared/bitstream-checksum.resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ResourceType } from './resource-type';

/**
* The resource type for BitstreamChecksum
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const BITSTREAM_CHECKSUM = new ResourceType('bitstreamchecksum');
9 changes: 9 additions & 0 deletions src/app/core/shared/bitstream.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { HALLink } from './hal-link.model';
import {BUNDLE} from './bundle.resource-type';
import {Bundle} from './bundle.model';
import { ChildHALResource } from './child-hal-resource.model';
import { BITSTREAM_CHECKSUM } from './bitstream-checksum.resource';
import { BitstreamChecksum } from './bitstream-checksum.model';

// Store number if the bitstream is stored in the both stores (S3 and local)
export const SYNCHRONIZED_STORES_NUMBER = 77;
Expand Down Expand Up @@ -53,6 +55,7 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
format: HALLink;
content: HALLink;
thumbnail: HALLink;
checksum: HALLink;
};

/**
Expand All @@ -76,6 +79,12 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
@link(BUNDLE)
bundle?: Observable<RemoteData<Bundle>>;

/**
* The checksum values fetched from the DB, local and S3 store.
*/
@link(BITSTREAM_CHECKSUM)
checksum?: Observable<RemoteData<BitstreamChecksum>>;

getParentLinkKey(): keyof this['_links'] {
return 'format';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export class PaginatedDragAndDropBitstreamListComponent extends AbstractPaginate
switchMap(() => this.bundleService.getBitstreams(
this.bundle.id,
paginatedOptions,
followLink('format')
followLink('format'),
followLink('checksum')
))
);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,61 @@
<div class="{{columnSizes.columns[4].buildClasses()}} row-element d-flex align-items-center justify-content-center">
<div class="float-left d-flex align-items-center overflow-hidden">
<span class="text-center">
<i [class]="bitstream.storeNumber === syncStoresNumber ? 'fas fa-check' : 'fas fa-times'"></i>
<i [class]="isBitstreamSynchronized() ? 'fas fa-check' : 'fas fa-times'"></i>
</span>
<span class="pl-1">|</span>
<div class="pl-1" [ngTemplateOutlet]="checksum"></div>
</div>
</div>
</ng-template>

<ng-template #checksum>
<div class="hover-container"
(mouseenter)="showChecksumValues = true"
(mouseleave)="showChecksumValues = false"
*ngVar="(checkSum$ | async) as bitstreamChecksum">
<i [class]="checksumsAreEqual(bitstreamChecksum) ? 'fas fa-check' : 'fas fa-times'"></i>
<i class="pl-2 fas fa-info-circle"
triggers="mouseenter:mouseleave"
[ngbPopover]="checksumPopover"
popoverTitle="Checksums"></i>
</div>
</ng-template>

<ng-template #checksumPopover>
<div *ngVar="(checkSum$ | async) as bitstreamChecksum">
<div>
<div class="font-weight-bold text-decoration-underline">
{{'item.edit.bitstreams.checksum.database' | translate}}
</div>
<div>
{{'item.edit.bitstreams.checksum.algorithm' | translate}} {{bitstreamChecksum.databaseChecksum.checkSumAlgorithm}}
</div>
<div>
{{'item.edit.bitstreams.checksum.value' | translate}} {{ bitstreamChecksum.databaseChecksum.value }}
</div>
</div>
<div>
<div class="font-weight-bold text-decoration-underline">
{{'item.edit.bitstreams.checksum.active-store' | translate}}
</div>
<div>
{{'item.edit.bitstreams.checksum.algorithm' | translate}} {{bitstreamChecksum.activeStore.checkSumAlgorithm}}
</div>
<div>
{{'item.edit.bitstreams.checksum.value' | translate}} {{ bitstreamChecksum.activeStore.value }}
</div>
</div>
<div *ngIf="isBitstreamSynchronized()">
<div class="font-weight-bold text-decoration-underline">
{{'item.edit.bitstreams.checksum.sync-store' | translate}}
</div>
<div>
{{'item.edit.bitstreams.checksum.algorithm' | translate}} {{bitstreamChecksum.synchronizedStore.checkSumAlgorithm}}
</div>
<div>
{{'item.edit.bitstreams.checksum.value' | translate}} {{ bitstreamChecksum.synchronizedStore.value }}
</div>
</div>
</div>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getBitstreamDownloadRoute } from '../../../../app-routing-paths';
import { By } from '@angular/platform-browser';
import { BrowserOnlyMockPipe } from '../../../../shared/testing/browser-only-mock.pipe';
import { RouterLinkDirectiveStub } from '../../../../shared/testing/router-link-directive.stub';
import { BitstreamChecksum } from '../../../../core/shared/bitstream-checksum.model';

let comp: ItemEditBitstreamComponent;
let fixture: ComponentFixture<ItemEditBitstreamComponent>;
Expand All @@ -29,6 +30,22 @@ const columnSizes = new ResponsiveTableSizes([
const format = Object.assign(new BitstreamFormat(), {
shortDescription: 'PDF'
});

const checksum = Object.assign(new BitstreamChecksum(), {
activeStore: {
checkSumAlgorithm: 'MD5',
value: '123'
},
synchronizedStore: {
checkSumAlgorithm: 'MD5',
value: '456'
},
databaseChecksum: {
checkSumAlgorithm: 'MD5',
value: '789'
}
});

const bitstream = Object.assign(new Bitstream(), {
uuid: 'bitstreamUUID',
name: 'Fake Bitstream',
Expand All @@ -38,7 +55,8 @@ const bitstream = Object.assign(new Bitstream(), {
content: { href: 'content-link' }
},

format: createSuccessfulRemoteDataObject$(format)
format: createSuccessfulRemoteDataObject$(format),
checksum: createSuccessfulRemoteDataObject$(checksum)
});
const fieldUpdate = {
field: bitstream,
Expand Down Expand Up @@ -82,7 +100,7 @@ describe('ItemEditBitstreamComponent', () => {
RouterLinkDirectiveStub
],
providers: [
{ provide: ObjectUpdatesService, useValue: objectUpdatesService }
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
], schemas: [
NO_ERRORS_SCHEMA
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import cloneDeep from 'lodash/cloneDeep';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { Observable } from 'rxjs';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../../../core/shared/operators';
import {
getRemoteDataPayload,
getFirstSucceededRemoteData,
getFirstCompletedRemoteData
} from '../../../../core/shared/operators';
import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model';
import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model';
import { getBitstreamDownloadRoute } from '../../../../app-routing-paths';
import { BitstreamChecksum, CheckSum } from '../../../../core/shared/bitstream-checksum.model';

@Component({
selector: 'ds-item-edit-bitstream',
Expand Down Expand Up @@ -64,9 +69,14 @@ export class ItemEditBitstreamComponent implements OnChanges, OnInit {
format$: Observable<BitstreamFormat>;

/**
* The value of the store number if the bitstream is stored in both stores (S3 and local)
* True on mouseover, false otherwise
*/
syncStoresNumber = SYNCHRONIZED_STORES_NUMBER;
showChecksumValues = false;

/**
* Object containing all checksums
*/
checkSum$: Observable<BitstreamChecksum>;

constructor(private objectUpdatesService: ObjectUpdatesService,
private dsoNameService: DSONameService,
Expand All @@ -89,6 +99,10 @@ export class ItemEditBitstreamComponent implements OnChanges, OnInit {
getFirstSucceededRemoteData(),
getRemoteDataPayload()
);
this.checkSum$ = this.bitstream.checksum.pipe(
getFirstCompletedRemoteData(),
getRemoteDataPayload()
);
}

/**
Expand Down Expand Up @@ -119,4 +133,37 @@ export class ItemEditBitstreamComponent implements OnChanges, OnInit {
return this.fieldUpdate.changeType >= 0;
}

/**
* Compare if two checksums are equal
*
* @param checksum1 e.g. DB checksum
* @param checksum2 e.g. Active store checksum (local or S3)
*/
compareChecksums(checksum1: CheckSum, checksum2: CheckSum): boolean {
return checksum1.value === checksum2.value && checksum1.checkSumAlgorithm === checksum2.checkSumAlgorithm;
}

/**
* Compare if all checksums are equal (DB, Active store, Synchronized store)
*
* @param bitstreamChecksum which contains all checksums
*/
checksumsAreEqual(bitstreamChecksum: BitstreamChecksum): boolean {
if (this.isBitstreamSynchronized()) {
// Compare DB and Active store checksums
// Compare DB and Synchronized and Active store checksums
return this.compareChecksums(bitstreamChecksum.databaseChecksum, bitstreamChecksum.activeStore) &&
this.compareChecksums(bitstreamChecksum.synchronizedStore, bitstreamChecksum.activeStore);
}
// Compare DB and Active store checksums
return this.compareChecksums(bitstreamChecksum.databaseChecksum, bitstreamChecksum.activeStore);
}

/**
* Check if the bitstream is stored in both stores (S3 and local)
*/
isBitstreamSynchronized() {
return this.bitstream?.storeNumber === SYNCHRONIZED_STORES_NUMBER;
}

}
3 changes: 2 additions & 1 deletion src/app/thumbnail/thumbnail.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ describe('ThumbnailComponent', () => {
bundle: { href: 'bundle.url' },
format: { href: 'format.url' },
content: { href: CONTENT },
thumbnail: undefined
thumbnail: undefined,
checksum: undefined
};
});

Expand Down
Loading

0 comments on commit 6be9911

Please sign in to comment.