-
Notifications
You must be signed in to change notification settings - Fork 22
Feat(datacite-tracker): replace effect with observable #299
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9419fd1
feat(datacite-tracker): Datacite tracker PoC
opaduchak 120eaca
fix(datacite-tracker): refined datacite tracker architecture
opaduchak 558bf01
fix(datacite-tracker): fix another round of review comments
opaduchak 25b8e34
fix(datacite-tracker): fix another round of review comments + reverte…
opaduchak 6a39ac8
feat(datacite-tracker): use observable instead of effects
opaduchak 4d2d639
fix(datacite-tracker): fixed current project observable to be private
opaduchak 013c7c7
fix(datacite-tracker): fixed observable type definitions
opaduchak 6f865d0
feat(datacite-tracker): added tests for datacite tracker service
opaduchak c115943
chore(datacite-tracker): added jsdoc for datacite tracker component a…
opaduchak 6d17c80
fix(datacite-tracker): fixed datacite service to actually return void
opaduchak 8c7fd15
fix(datacite-tracker): fixed test for void type
opaduchak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/app/shared/components/datacite-tracker/datacite-tracker.component.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { filter, Observable, switchMap, take } from 'rxjs'; | ||
|
|
||
| import { inject, Injectable } from '@angular/core'; | ||
|
|
||
| import { DataciteService } from '@shared/services/datacite/datacite.service'; | ||
|
|
||
| @Injectable() | ||
| export abstract class DataciteTrackerComponent { | ||
| private dataciteService = inject(DataciteService); | ||
|
|
||
| /** | ||
| * Abstract method to retrieve the DOI (Digital Object Identifier) of the resource. | ||
| * Must be implemented by subclasses. | ||
| * | ||
| * @returns An Observable that emits a string DOI or null if unavailable. | ||
| */ | ||
| protected abstract getDoi(): Observable<string | null>; | ||
|
|
||
| /** | ||
| * Sets up a one-time effect to log a "view" event to Datacite for the resource DOI. | ||
| * It waits until the DOI is available, takes the first non-null value, | ||
| * and then calls `DataciteService.logView`. | ||
| * | ||
| * @returns An Observable that completes after logging the view. | ||
| */ | ||
| protected setupDataciteViewTrackerEffect(): Observable<void> { | ||
| return this.getDoi().pipe( | ||
| take(1), | ||
| filter((doi): doi is string => !!doi), | ||
| switchMap((doi) => this.dataciteService.logView(doi)) | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export const enum DataciteEvent { | ||
| VIEW = 'view', | ||
| DOWNLOAD = 'download', | ||
| } |
117 changes: 117 additions & 0 deletions
117
src/app/shared/services/datacite/datacite.service.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| import { provideHttpClient } from '@angular/common/http'; | ||
| import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; | ||
| import { TestBed } from '@angular/core/testing'; | ||
|
|
||
| import { ENVIRONMENT } from '@core/constants/environment.token'; | ||
| import { DataciteEvent } from '@shared/models/datacite/datacite-event.enum'; | ||
|
|
||
| import { DataciteService } from './datacite.service'; | ||
|
|
||
| describe('DataciteService', () => { | ||
| let service: DataciteService; | ||
| let httpMock: HttpTestingController; | ||
|
|
||
| const dataciteTrackerAddress = 'https://tracker.test'; | ||
| const dataciteTrackerRepoId = 'repo-123'; | ||
| describe('with proper configuration', () => { | ||
| beforeEach(() => { | ||
| TestBed.configureTestingModule({ | ||
| providers: [ | ||
| DataciteService, | ||
| provideHttpClient(), | ||
| provideHttpClientTesting(), | ||
| { | ||
| provide: ENVIRONMENT, | ||
| useValue: { | ||
| dataciteTrackerRepoId, | ||
| dataciteTrackerAddress, | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| service = TestBed.inject(DataciteService); | ||
| httpMock = TestBed.inject(HttpTestingController); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| httpMock.verify(); | ||
| }); | ||
|
|
||
| it('logView should POST with correct payload', () => { | ||
| const doi = '10.1234/abcd'; | ||
| service.logView(doi).subscribe({ | ||
| next: (result) => expect(result).toBeUndefined(), | ||
| }); | ||
|
|
||
| const req = httpMock.expectOne(dataciteTrackerAddress); | ||
| expect(req.request.method).toBe('POST'); | ||
| expect(req.request.body).toEqual({ | ||
| n: DataciteEvent.VIEW, | ||
| u: window.location.href, | ||
| i: dataciteTrackerRepoId, | ||
| p: doi, | ||
| }); | ||
| expect(req.request.headers.get('Content-Type')).toBe('application/json'); | ||
| req.flush({}); | ||
| }); | ||
|
|
||
| it('logDownload should POST with correct payload', () => { | ||
| const doi = '10.1234/abcd'; | ||
| service.logDownload(doi).subscribe({ | ||
| next: (result) => expect(result).toBeUndefined(), | ||
| }); | ||
|
|
||
| const req = httpMock.expectOne(dataciteTrackerAddress); | ||
| expect(req.request.body).toEqual({ | ||
| n: DataciteEvent.DOWNLOAD, | ||
| u: window.location.href, | ||
| i: dataciteTrackerRepoId, | ||
| p: doi, | ||
| }); | ||
| req.flush({}); | ||
| }); | ||
|
|
||
| it('should return EMPTY when doi is missing', (done: () => void) => { | ||
| service.logView('').subscribe({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are not actually testing the result to be void. It needs a next method. |
||
| next: (result) => expect(result).toBeUndefined(), | ||
| complete: () => done(), | ||
| }); | ||
| httpMock.expectNone(dataciteTrackerAddress); | ||
| }); | ||
| }); | ||
|
|
||
| describe('on local setup (without dataciteTrackerRepoId configured)', () => { | ||
| beforeEach(() => { | ||
| TestBed.configureTestingModule({ | ||
| providers: [ | ||
| DataciteService, | ||
| provideHttpClient(), | ||
| provideHttpClientTesting(), | ||
| { | ||
| provide: ENVIRONMENT, | ||
| useValue: { | ||
| dataciteTrackerRepoId: null, | ||
| dataciteTrackerAddress: dataciteTrackerAddress, | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| service = TestBed.inject(DataciteService); | ||
| httpMock = TestBed.inject(HttpTestingController); | ||
| }); | ||
|
|
||
| it('should return EMPTY when dataciteTrackerRepoId is missing', (done: () => void) => { | ||
| service.logView('10.1234/abcd').subscribe({ | ||
| next: (result) => expect(result).toBeUndefined(), | ||
| complete: () => done(), | ||
| }); | ||
| httpMock.expectNone(dataciteTrackerAddress); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| httpMock.verify(); | ||
| }); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { EMPTY, map, Observable } from 'rxjs'; | ||
|
|
||
| import { HttpClient } from '@angular/common/http'; | ||
| import { inject, Injectable } from '@angular/core'; | ||
|
|
||
| import { ENVIRONMENT } from '@core/constants/environment.token'; | ||
| import { DataciteEvent } from '@shared/models/datacite/datacite-event.enum'; | ||
|
|
||
| @Injectable({ | ||
| providedIn: 'root', | ||
| }) | ||
| export class DataciteService { | ||
| #http: HttpClient = inject(HttpClient); | ||
| #environment = inject(ENVIRONMENT); | ||
|
|
||
| /** | ||
| * Logs a "view" event for a given DOI to the Datacite tracker. | ||
| * If the DOI is null/empty or the tracker repository ID is not configured, | ||
| * (in most cases, due to being used in dev environment), | ||
| * returns an empty observable. | ||
| * | ||
| * @param doi - The DOI (Digital Object Identifier) of the resource. | ||
| * @returns An Observable that completes when the request is sent. | ||
| */ | ||
| logView(doi: string): Observable<void> { | ||
| return this.logActivity(DataciteEvent.VIEW, doi); | ||
| } | ||
|
|
||
| /** | ||
| * Logs a "download" event for a given DOI to the Datacite tracker. | ||
| * If the DOI is null/empty or the tracker repository ID is not configured | ||
| * (in most cases, due to being used in dev environment), | ||
| * returns an empty observable. | ||
| * | ||
| * @param doi - The DOI (Digital Object Identifier) of the resource. | ||
| * @returns An Observable that completes when the request is sent. | ||
| */ | ||
| logDownload(doi: string): Observable<void> { | ||
| return this.logActivity(DataciteEvent.DOWNLOAD, doi); | ||
| } | ||
|
|
||
| /** | ||
| * Internal helper to log a specific Datacite event for a given DOI. | ||
| * | ||
| * @param event - The Datacite event type (VIEW or DOWNLOAD). | ||
| * @param doi - The DOI (Digital Object Identifier) of the resource. | ||
| * @returns An Observable that completes when the HTTP POST is sent, | ||
| * or EMPTY if DOI or repo ID is missing. | ||
| */ | ||
| private logActivity(event: DataciteEvent, doi: string): Observable<void> { | ||
| if (!doi || !this.#environment.dataciteTrackerRepoId) { | ||
| return EMPTY; | ||
| } | ||
| const payload = { | ||
| n: event, | ||
| u: window.location.href, | ||
| i: this.#environment.dataciteTrackerRepoId, | ||
| p: doi, | ||
| }; | ||
| const headers = { | ||
| 'Content-Type': 'application/json', | ||
| }; | ||
| return this.#http.post(this.#environment.dataciteTrackerAddress, payload, { headers }).pipe( | ||
| map(() => { | ||
| return; | ||
| }) | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Does this need to be port 5000?
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 thought it was better, but I was mistaken, because angular still relies on
osf-webcontainer in several key places (such as auth). On the other hand, with angular being hosted on 4200 port we'll need to ensure thathttp://localhost:4200in inALLOWED_HOSTSand is accounted for in CORS policies in all local services