From 1760afafd527ce59d6acf9022e2e1d9f0e2179f7 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 09:50:37 +1000 Subject: [PATCH 01/29] Created analysis jobs service --- src/app/services/baw-api/ServiceProviders.ts | 8 +++ src/app/services/baw-api/ServiceTokens.ts | 4 ++ .../baw-api/analysis-jobs.service.spec.ts | 56 +++++++++++++++++++ .../services/baw-api/analysis-jobs.service.ts | 55 ++++++++++++++++++ src/app/test.helper.ts | 28 +++++----- 5 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 src/app/services/baw-api/analysis-jobs.service.spec.ts create mode 100644 src/app/services/baw-api/analysis-jobs.service.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 837711341..1a702a834 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -1,10 +1,15 @@ import { AppConfigService } from "@services/app-config/app-config.service"; import { accountResolvers, AccountService } from "./account.service"; +import { + analysisJobResolvers, + AnalysisJobsService, +} from "./analysis-jobs.service"; import { projectResolvers, ProjectsService } from "./projects.service"; import { scriptResolvers, ScriptsService } from "./scripts.service"; import { SecurityService } from "./security.service"; import { ACCOUNT, + ANALYSIS_JOB, PROJECT, SCRIPT, SECURITY, @@ -27,6 +32,7 @@ import { userResolvers, UserService } from "./user.service"; const services = [ AppConfigService, AccountService, + AnalysisJobsService, ProjectsService, ScriptsService, SecurityService, @@ -36,6 +42,7 @@ const services = [ TagGroupService, UserService, { provide: ACCOUNT.token, useExisting: AccountService }, + { provide: ANALYSIS_JOB.token, useExisting: AnalysisJobsService }, { provide: PROJECT.token, useExisting: ProjectsService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, @@ -48,6 +55,7 @@ const services = [ const resolvers = [ ...accountResolvers.providers, + ...analysisJobResolvers.providers, ...projectResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 68edf1f21..045845174 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -7,6 +7,7 @@ import type { ShallowSitesService, SitesService } from "./sites.service"; import type { TagGroupService } from "./tag-group.service"; import type { TagsService } from "./tags.service"; import type { UserService } from "./user.service"; +import type { AnalysisJobsService } from "./analysis-jobs.service"; // Wrapper because of https://github.com/angular/angular/issues/36736 export class ServiceToken { @@ -20,6 +21,9 @@ export class ServiceToken { } export const ACCOUNT = new ServiceToken("ACCOUNT_SERVICE"); +export const ANALYSIS_JOB = new ServiceToken( + "ANALYSIS_JOBS_SERVICE" +); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); export const SECURITY = new ServiceToken("SECURITY_SERVICE"); diff --git a/src/app/services/baw-api/analysis-jobs.service.spec.ts b/src/app/services/baw-api/analysis-jobs.service.spec.ts new file mode 100644 index 000000000..4c59e455a --- /dev/null +++ b/src/app/services/baw-api/analysis-jobs.service.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { AnalysisJob } from "@models/AnalysisJob"; +import { testAppInitializer } from "src/app/test.helper"; +import { AnalysisJobsService } from "./analysis-jobs.service"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; + +describe("AnalysisJobsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + AnalysisJobsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(AnalysisJobsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/analysis_jobs/"); + validateApiFilter("/analysis_jobs/filter"); + validateApiShow( + "/analysis_jobs/5", + 5, + new AnalysisJob({ id: 5 }) + ); + validateApiCreate( + "/analysis_jobs/", + new AnalysisJob({ id: 5 }) + ); + validateApiUpdate( + "/analysis_jobs/5", + new AnalysisJob({ id: 5 }) + ); + validateApiDestroy( + "/analysis_jobs/5", + 5, + new AnalysisJob({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/analysis-jobs.service.ts b/src/app/services/baw-api/analysis-jobs.service.ts new file mode 100644 index 000000000..53843fd5e --- /dev/null +++ b/src/app/services/baw-api/analysis-jobs.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { AnalysisJob } from "@models/AnalysisJob"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const analysisJobId: IdParamOptional = id; +const endpoint = stringTemplate`/analysis_jobs/${analysisJobId}${option}`; + +/** + * Analysis Jobs Service. + * Handles API routes pertaining to analysis job models. + */ +@Injectable() +export class AnalysisJobsService extends StandardApi { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, AnalysisJob); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: AnalysisJob): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: AnalysisJob): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const analysisJobResolvers = new Resolvers< + AnalysisJob, + AnalysisJobsService +>([AnalysisJobsService], "analysisJobId").create("AnalysisJob"); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index c2d7521ff..984082607 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -4,22 +4,22 @@ import { BehaviorSubject } from "rxjs"; import { API_CONFIG, API_ROOT, - CMS_ROOT + CMS_ROOT, } from "./helpers/app-initializer/app-initializer"; import { AppConfigService } from "./services/app-config/app-config.service"; import { AppConfigMockService, - testApiConfig + testApiConfig, } from "./services/app-config/appConfigMock.service"; import { AccountService } from "./services/baw-api/account.service"; import { BawApiInterceptor } from "./services/baw-api/api.interceptor.service"; import { BawApiService, - STUB_MODEL_BUILDER + STUB_MODEL_BUILDER, } from "./services/baw-api/baw-api.service"; import { MockBawApiService, - MockModel + MockModel, } from "./services/baw-api/mock/baseApiMock.service"; import { MockSecurityService } from "./services/baw-api/mock/securityMock.service"; import { MockShowApiService } from "./services/baw-api/mock/showApiMock.service"; @@ -30,11 +30,12 @@ import { ScriptsService } from "./services/baw-api/scripts.service"; import { SecurityService } from "./services/baw-api/security.service"; import { ShallowSitesService, - SitesService + SitesService, } from "./services/baw-api/sites.service"; import { TagGroupService } from "./services/baw-api/tag-group.service"; import { TagsService } from "./services/baw-api/tags.service"; import { UserService } from "./services/baw-api/user.service"; +import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; /** * Create mock initializer values @@ -42,22 +43,22 @@ import { UserService } from "./services/baw-api/user.service"; export const testAppInitializer = [ { provide: API_ROOT, - useValue: testApiConfig.environment.apiRoot + useValue: testApiConfig.environment.apiRoot, }, { provide: CMS_ROOT, - useValue: testApiConfig.environment.cmsRoot + useValue: testApiConfig.environment.cmsRoot, }, { provide: API_CONFIG, - useValue: new Promise(resolve => { + useValue: new Promise((resolve) => { resolve(testApiConfig); - }) + }), }, { provide: AppConfigService, - useClass: AppConfigMockService - } + useClass: AppConfigMockService, + }, ]; /** @@ -68,19 +69,20 @@ export const testBawServices = [ { provide: HTTP_INTERCEPTORS, useClass: BawApiInterceptor, - multi: true + multi: true, }, { provide: STUB_MODEL_BUILDER, useValue: MockModel }, { provide: BawApiService, useClass: MockBawApiService }, { provide: SecurityService, useClass: MockSecurityService }, { provide: AccountService, useClass: MockStandardApiService }, + { provide: AnalysisJobsService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, { provide: ShallowSitesService, useClass: MockStandardApiService }, { provide: TagsService, useClass: MockStandardApiService }, { provide: TagGroupService, useClass: MockStandardApiService }, - { provide: UserService, useClass: MockShowApiService } + { provide: UserService, useClass: MockShowApiService }, ]; /** From 9c48e5243c3a45f5837a60c7ad4f101a418a70c5 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 10:22:32 +1000 Subject: [PATCH 02/29] Created audio event service --- src/app/services/baw-api/ServiceProviders.ts | 8 +++ src/app/services/baw-api/ServiceTokens.ts | 6 +- .../baw-api/audio-events.service.spec.ts | 69 ++++++++++++++++++ .../services/baw-api/audio-events.service.ts | 72 +++++++++++++++++++ .../services/baw-api/sites.service.spec.ts | 1 + src/app/test.helper.ts | 4 +- 6 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 src/app/services/baw-api/audio-events.service.spec.ts create mode 100644 src/app/services/baw-api/audio-events.service.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 1a702a834..ab82f11dd 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -4,12 +4,17 @@ import { analysisJobResolvers, AnalysisJobsService, } from "./analysis-jobs.service"; +import { + audioEventResolvers, + AudioEventsService, +} from "./audio-events.service"; import { projectResolvers, ProjectsService } from "./projects.service"; import { scriptResolvers, ScriptsService } from "./scripts.service"; import { SecurityService } from "./security.service"; import { ACCOUNT, ANALYSIS_JOB, + AUDIO_EVENT, PROJECT, SCRIPT, SECURITY, @@ -33,6 +38,7 @@ const services = [ AppConfigService, AccountService, AnalysisJobsService, + AudioEventsService, ProjectsService, ScriptsService, SecurityService, @@ -43,6 +49,7 @@ const services = [ UserService, { provide: ACCOUNT.token, useExisting: AccountService }, { provide: ANALYSIS_JOB.token, useExisting: AnalysisJobsService }, + { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: PROJECT.token, useExisting: ProjectsService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, @@ -56,6 +63,7 @@ const services = [ const resolvers = [ ...accountResolvers.providers, ...analysisJobResolvers.providers, + ...audioEventResolvers.providers, ...projectResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 045845174..51c3a935d 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -1,5 +1,7 @@ import { InjectionToken } from "@angular/core"; import type { AccountService } from "./account.service"; +import type { AnalysisJobsService } from "./analysis-jobs.service"; +import type { AudioEventsService } from "./audio-events.service"; import type { ProjectsService } from "./projects.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; @@ -7,7 +9,6 @@ import type { ShallowSitesService, SitesService } from "./sites.service"; import type { TagGroupService } from "./tag-group.service"; import type { TagsService } from "./tags.service"; import type { UserService } from "./user.service"; -import type { AnalysisJobsService } from "./analysis-jobs.service"; // Wrapper because of https://github.com/angular/angular/issues/36736 export class ServiceToken { @@ -24,6 +25,9 @@ export const ACCOUNT = new ServiceToken("ACCOUNT_SERVICE"); export const ANALYSIS_JOB = new ServiceToken( "ANALYSIS_JOBS_SERVICE" ); +export const AUDIO_EVENT = new ServiceToken( + "AUDIO_EVENTS_SERVICE" +); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); export const SECURITY = new ServiceToken("SECURITY_SERVICE"); diff --git a/src/app/services/baw-api/audio-events.service.spec.ts b/src/app/services/baw-api/audio-events.service.spec.ts new file mode 100644 index 000000000..3815fb516 --- /dev/null +++ b/src/app/services/baw-api/audio-events.service.spec.ts @@ -0,0 +1,69 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { AudioEvent } from "@models/AudioEvent"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { AudioEventsService } from "./audio-events.service"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; + +describe("AudioEventsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + AudioEventsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(AudioEventsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList( + "/analysis_jobs/5/audio_events/", + undefined, + 5 + ); + validateApiFilter( + "/analysis_jobs/5/audio_events/filter", + undefined, + undefined, + 5 + ); + validateApiShow( + "/analysis_jobs/5/audio_events/10", + 10, + new AudioEvent({ id: 10 }), + 5 + ); + validateApiCreate( + "/analysis_jobs/5/audio_events/", + new AudioEvent({ id: 10 }), + 5 + ); + validateApiUpdate( + "/analysis_jobs/5/audio_events/10", + new AudioEvent({ id: 10 }), + 5 + ); + validateApiDestroy( + "/analysis_jobs/5/audio_events/10", + 10, + new AudioEvent({ id: 10 }), + 5 + ); +}); diff --git a/src/app/services/baw-api/audio-events.service.ts b/src/app/services/baw-api/audio-events.service.ts new file mode 100644 index 000000000..4c7a85852 --- /dev/null +++ b/src/app/services/baw-api/audio-events.service.ts @@ -0,0 +1,72 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { AnalysisJob } from "@models/AnalysisJob"; +import { AudioEvent } from "@models/AudioEvent"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParam, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const analysisJobId: IdParam = id; +const audioEventId: IdParamOptional = id; +const endpoint = stringTemplate`/analysis_jobs/${analysisJobId}/audio_events/${audioEventId}${option}`; + +@Injectable() +export class AudioEventsService extends StandardApi< + AudioEvent, + [IdOr] +> { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, AudioEvent); + } + + list(analysis: IdOr): Observable { + return this.apiList(endpoint(analysis, Empty, Empty)); + } + filter( + filters: Filters, + analysis: IdOr + ): Observable { + return this.apiFilter(endpoint(analysis, Empty, Filter), filters); + } + show( + model: IdOr, + analysis: IdOr + ): Observable { + return this.apiShow(endpoint(analysis, model, Empty)); + } + create( + model: AudioEvent, + analysis: IdOr + ): Observable { + return this.apiCreate(endpoint(analysis, Empty, Empty), model); + } + update( + model: AudioEvent, + analysis: IdOr + ): Observable { + return this.apiUpdate(endpoint(analysis, model, Empty), model); + } + destroy( + model: IdOr, + analysis: IdOr + ): Observable { + return this.apiDestroy(endpoint(analysis, model, Empty)); + } +} + +export const audioEventResolvers = new Resolvers< + AudioEvent, + AudioEventsService +>([AudioEventsService], "audioEventId", ["analysisJobId"]).create("AudioEvent"); diff --git a/src/app/services/baw-api/sites.service.spec.ts b/src/app/services/baw-api/sites.service.spec.ts index c375b5148..ffaf0b85b 100644 --- a/src/app/services/baw-api/sites.service.spec.ts +++ b/src/app/services/baw-api/sites.service.spec.ts @@ -28,6 +28,7 @@ describe("SitesService", function () { this.service = TestBed.inject(SitesService); }); + it("should be created", function () { expect(this.service).toBeTruthy(); }); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index 984082607..484e51f5b 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -1,5 +1,7 @@ import { HTTP_INTERCEPTORS } from "@angular/common/http"; import { Params } from "@angular/router"; +import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; +import { AudioEventsService } from "@baw-api/audio-events.service"; import { BehaviorSubject } from "rxjs"; import { API_CONFIG, @@ -35,7 +37,6 @@ import { import { TagGroupService } from "./services/baw-api/tag-group.service"; import { TagsService } from "./services/baw-api/tags.service"; import { UserService } from "./services/baw-api/user.service"; -import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; /** * Create mock initializer values @@ -76,6 +77,7 @@ export const testBawServices = [ { provide: SecurityService, useClass: MockSecurityService }, { provide: AccountService, useClass: MockStandardApiService }, { provide: AnalysisJobsService, useClass: MockStandardApiService }, + { provide: AudioEventsService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, From 1c4a709756d91a6a4d20ef5066de147554c8c758 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 12:03:36 +1000 Subject: [PATCH 03/29] Created audio event comment service --- src/app/services/baw-api/ServiceProviders.ts | 11 +++ src/app/services/baw-api/ServiceTokens.ts | 4 + .../audio-event-comments.service.spec.ts | 69 ++++++++++++++ .../baw-api/audio-event-comments.service.ts | 92 +++++++++++++++++++ src/app/test.helper.ts | 2 + 5 files changed, 178 insertions(+) create mode 100644 src/app/services/baw-api/audio-event-comments.service.spec.ts create mode 100644 src/app/services/baw-api/audio-event-comments.service.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index ab82f11dd..9ddc13401 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -4,6 +4,10 @@ import { analysisJobResolvers, AnalysisJobsService, } from "./analysis-jobs.service"; +import { + audioEventCommentResolvers, + AudioEventCommentsService, +} from "./audio-event-comments.service"; import { audioEventResolvers, AudioEventsService, @@ -15,6 +19,7 @@ import { ACCOUNT, ANALYSIS_JOB, AUDIO_EVENT, + AUDIO_EVENT_COMMENT, PROJECT, SCRIPT, SECURITY, @@ -38,6 +43,7 @@ const services = [ AppConfigService, AccountService, AnalysisJobsService, + AudioEventCommentsService, AudioEventsService, ProjectsService, ScriptsService, @@ -49,6 +55,10 @@ const services = [ UserService, { provide: ACCOUNT.token, useExisting: AccountService }, { provide: ANALYSIS_JOB.token, useExisting: AnalysisJobsService }, + { + provide: AUDIO_EVENT_COMMENT.token, + useExisting: AudioEventCommentsService, + }, { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: PROJECT.token, useExisting: ProjectsService }, { provide: SCRIPT.token, useExisting: ScriptsService }, @@ -63,6 +73,7 @@ const services = [ const resolvers = [ ...accountResolvers.providers, ...analysisJobResolvers.providers, + ...audioEventCommentResolvers.providers, ...audioEventResolvers.providers, ...projectResolvers.providers, ...scriptResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 51c3a935d..8e0eae1a4 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -9,6 +9,7 @@ import type { ShallowSitesService, SitesService } from "./sites.service"; import type { TagGroupService } from "./tag-group.service"; import type { TagsService } from "./tags.service"; import type { UserService } from "./user.service"; +import { AudioEventCommentsService } from "./audio-event-comments.service"; // Wrapper because of https://github.com/angular/angular/issues/36736 export class ServiceToken { @@ -28,6 +29,9 @@ export const ANALYSIS_JOB = new ServiceToken( export const AUDIO_EVENT = new ServiceToken( "AUDIO_EVENTS_SERVICE" ); +export const AUDIO_EVENT_COMMENT = new ServiceToken( + "AUDIO_EVENT_COMMENTS_SERVICE" +); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); export const SECURITY = new ServiceToken("SECURITY_SERVICE"); diff --git a/src/app/services/baw-api/audio-event-comments.service.spec.ts b/src/app/services/baw-api/audio-event-comments.service.spec.ts new file mode 100644 index 000000000..2bc4340df --- /dev/null +++ b/src/app/services/baw-api/audio-event-comments.service.spec.ts @@ -0,0 +1,69 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { AudioEventComment } from "@models/AudioEventComment"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { AudioEventCommentsService } from "./audio-event-comments.service"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; + +describe("AudioEventCommentsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + AudioEventCommentsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(AudioEventCommentsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList( + "/analysis_jobs/5/comments/", + undefined, + 5 + ); + validateApiFilter( + "/analysis_jobs/5/comments/filter", + undefined, + undefined, + 5 + ); + validateApiShow( + "/analysis_jobs/5/comments/10", + 10, + new AudioEventComment({ id: 10 }), + 5 + ); + validateApiCreate( + "/analysis_jobs/5/comments/", + new AudioEventComment({ id: 10 }), + 5 + ); + validateApiUpdate( + "/analysis_jobs/5/comments/10", + new AudioEventComment({ id: 10 }), + 5 + ); + validateApiDestroy( + "/analysis_jobs/5/comments/10", + 10, + new AudioEventComment({ id: 10 }), + 5 + ); +}); diff --git a/src/app/services/baw-api/audio-event-comments.service.ts b/src/app/services/baw-api/audio-event-comments.service.ts new file mode 100644 index 000000000..8fb38c6c7 --- /dev/null +++ b/src/app/services/baw-api/audio-event-comments.service.ts @@ -0,0 +1,92 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { AnalysisJob } from "@models/AnalysisJob"; +import { AudioEventComment } from "@models/AudioEventComment"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParam, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const analysisJobId: IdParam = id; +const audioEventCommentId: IdParamOptional = id; +const endpoint = stringTemplate`/analysis_jobs/${analysisJobId}/comments/${audioEventCommentId}${option}`; + +@Injectable() +export class AudioEventCommentsService extends StandardApi< + AudioEventComment, + [IdOr] +> { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, AudioEventComment); + } + + list(analysis: IdOr): Observable { + return this.apiList(endpoint(analysis, Empty, Empty)); + } + apiList(arg0: any): any { + throw new Error("Method not implemented."); + } + filter( + filters: Filters, + analysis: IdOr + ): Observable { + return this.apiFilter(endpoint(analysis, Empty, Filter), filters); + } + apiFilter(arg0: any, filters: any): any { + throw new Error("Method not implemented."); + } + show( + model: IdOr, + analysis: IdOr + ): Observable { + return this.apiShow(endpoint(analysis, model, Empty)); + } + apiShow(arg0: any): any { + throw new Error("Method not implemented."); + } + create( + model: AudioEventComment, + analysis: IdOr + ): Observable { + return this.apiCreate(endpoint(analysis, Empty, Empty), model); + } + apiCreate(arg0: any, model: AudioEventComment): any { + throw new Error("Method not implemented."); + } + update( + model: AudioEventComment, + analysis: IdOr + ): Observable { + return this.apiUpdate(endpoint(analysis, model, Empty), model); + } + apiUpdate(arg0: any, model: AudioEventComment): any { + throw new Error("Method not implemented."); + } + destroy( + model: IdOr, + analysis: IdOr + ): Observable { + return this.apiDestroy(endpoint(analysis, model, Empty)); + } + apiDestroy(arg0: any): any { + throw new Error("Method not implemented."); + } +} + +export const audioEventCommentResolvers = new Resolvers< + AudioEventComment, + AudioEventCommentsService +>([AudioEventCommentsService], "audioEventCommentId", ["analysisJobId"]).create( + "AudioEventComment" +); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index 484e51f5b..ebae4cfcd 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -1,6 +1,7 @@ import { HTTP_INTERCEPTORS } from "@angular/common/http"; import { Params } from "@angular/router"; import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; +import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service"; import { AudioEventsService } from "@baw-api/audio-events.service"; import { BehaviorSubject } from "rxjs"; import { @@ -77,6 +78,7 @@ export const testBawServices = [ { provide: SecurityService, useClass: MockSecurityService }, { provide: AccountService, useClass: MockStandardApiService }, { provide: AnalysisJobsService, useClass: MockStandardApiService }, + { provide: AudioEventCommentsService, useClass: MockStandardApiService }, { provide: AudioEventsService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, From 64f16b08b1a8e5c64c915bb366e393b89911aa7a Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 12:51:20 +1000 Subject: [PATCH 04/29] Created bookmark service --- src/app/models/Bookmark.ts | 2 +- src/app/services/baw-api/ServiceProviders.ts | 5 ++ src/app/services/baw-api/ServiceTokens.ts | 4 +- .../baw-api/bookmarks.service.spec.ts | 56 +++++++++++++++++++ src/app/services/baw-api/bookmarks.service.ts | 55 ++++++++++++++++++ src/app/test.helper.ts | 2 + 6 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/app/services/baw-api/bookmarks.service.spec.ts create mode 100644 src/app/services/baw-api/bookmarks.service.ts diff --git a/src/app/models/Bookmark.ts b/src/app/models/Bookmark.ts index a9f011e9b..598cd1207 100644 --- a/src/app/models/Bookmark.ts +++ b/src/app/models/Bookmark.ts @@ -64,7 +64,7 @@ export class Bookmark extends AbstractModel implements IBookmark { public get viewUrl(): string { return "/BROKEN_LINK"; - //return `https://www.ecosounds.org/listen/${this.audioRecordingId}?start=${this.offsetSeconds}&end=${???}`; + // return `https://www.ecosounds.org/listen/${this.audioRecordingId}?start=${this.offsetSeconds}&end=${???}`; } } diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 9ddc13401..748ee45f9 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -12,6 +12,7 @@ import { audioEventResolvers, AudioEventsService, } from "./audio-events.service"; +import { bookmarkResolvers, BookmarksService } from "./bookmarks.service"; import { projectResolvers, ProjectsService } from "./projects.service"; import { scriptResolvers, ScriptsService } from "./scripts.service"; import { SecurityService } from "./security.service"; @@ -20,6 +21,7 @@ import { ANALYSIS_JOB, AUDIO_EVENT, AUDIO_EVENT_COMMENT, + BOOKMARK, PROJECT, SCRIPT, SECURITY, @@ -45,6 +47,7 @@ const services = [ AnalysisJobsService, AudioEventCommentsService, AudioEventsService, + BookmarksService, ProjectsService, ScriptsService, SecurityService, @@ -60,6 +63,7 @@ const services = [ useExisting: AudioEventCommentsService, }, { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, + { provide: BOOKMARK.token, useExisting: BookmarksService }, { provide: PROJECT.token, useExisting: ProjectsService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, @@ -75,6 +79,7 @@ const resolvers = [ ...analysisJobResolvers.providers, ...audioEventCommentResolvers.providers, ...audioEventResolvers.providers, + ...bookmarkResolvers.providers, ...projectResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 8e0eae1a4..9d9186b40 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -1,7 +1,9 @@ import { InjectionToken } from "@angular/core"; import type { AccountService } from "./account.service"; import type { AnalysisJobsService } from "./analysis-jobs.service"; +import type { AudioEventCommentsService } from "./audio-event-comments.service"; import type { AudioEventsService } from "./audio-events.service"; +import type { BookmarksService } from "./bookmarks.service"; import type { ProjectsService } from "./projects.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; @@ -9,7 +11,6 @@ import type { ShallowSitesService, SitesService } from "./sites.service"; import type { TagGroupService } from "./tag-group.service"; import type { TagsService } from "./tags.service"; import type { UserService } from "./user.service"; -import { AudioEventCommentsService } from "./audio-event-comments.service"; // Wrapper because of https://github.com/angular/angular/issues/36736 export class ServiceToken { @@ -32,6 +33,7 @@ export const AUDIO_EVENT = new ServiceToken( export const AUDIO_EVENT_COMMENT = new ServiceToken( "AUDIO_EVENT_COMMENTS_SERVICE" ); +export const BOOKMARK = new ServiceToken("BOOKMARKS_SERVICE"); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); export const SECURITY = new ServiceToken("SECURITY_SERVICE"); diff --git a/src/app/services/baw-api/bookmarks.service.spec.ts b/src/app/services/baw-api/bookmarks.service.spec.ts new file mode 100644 index 000000000..78013e602 --- /dev/null +++ b/src/app/services/baw-api/bookmarks.service.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Bookmark } from "@models/Bookmark"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { BookmarksService } from "./bookmarks.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; + +describe("BookmarksService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + BookmarksService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(BookmarksService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/bookmarks/"); + validateApiFilter("/bookmarks/filter"); + validateApiShow( + "/bookmarks/5", + 5, + new Bookmark({ id: 5 }) + ); + validateApiCreate( + "/bookmarks/", + new Bookmark({ id: 5 }) + ); + validateApiUpdate( + "/bookmarks/5", + new Bookmark({ id: 5 }) + ); + validateApiDestroy( + "/bookmarks/5", + 5, + new Bookmark({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/bookmarks.service.ts b/src/app/services/baw-api/bookmarks.service.ts new file mode 100644 index 000000000..146753308 --- /dev/null +++ b/src/app/services/baw-api/bookmarks.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable, Injector } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Bookmark } from "@models/Bookmark"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const bookmarkId: IdParamOptional = id; +const endpoint = stringTemplate`/bookmarks/${bookmarkId}${option}`; + +@Injectable() +export class BookmarksService extends StandardApi { + constructor( + http: HttpClient, + @Inject(API_ROOT) apiRoot: string, + injector: Injector + ) { + super(http, apiRoot, Bookmark, injector); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: Bookmark): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: Bookmark): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const bookmarkResolvers = new Resolvers( + [BookmarksService], + "bookmarkId" +).create("Bookmark"); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index ebae4cfcd..ecb1d5eb8 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -3,6 +3,7 @@ import { Params } from "@angular/router"; import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service"; import { AudioEventsService } from "@baw-api/audio-events.service"; +import { BookmarksService } from "@baw-api/bookmarks.service"; import { BehaviorSubject } from "rxjs"; import { API_CONFIG, @@ -80,6 +81,7 @@ export const testBawServices = [ { provide: AnalysisJobsService, useClass: MockStandardApiService }, { provide: AudioEventCommentsService, useClass: MockStandardApiService }, { provide: AudioEventsService, useClass: MockStandardApiService }, + { provide: BookmarksService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, From 458c009e08ff2a3db44faae0dfcf5503b3f40bcf Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 13:00:38 +1000 Subject: [PATCH 05/29] Created saved search service --- src/app/services/baw-api/ServiceProviders.ts | 8 +++ src/app/services/baw-api/ServiceTokens.ts | 4 ++ .../baw-api/saved-searches.service.spec.ts | 58 +++++++++++++++++++ .../baw-api/saved-searches.service.ts | 55 ++++++++++++++++++ src/app/test.helper.ts | 2 + 5 files changed, 127 insertions(+) create mode 100644 src/app/services/baw-api/saved-searches.service.spec.ts create mode 100644 src/app/services/baw-api/saved-searches.service.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 748ee45f9..503b2a738 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -14,6 +14,10 @@ import { } from "./audio-events.service"; import { bookmarkResolvers, BookmarksService } from "./bookmarks.service"; import { projectResolvers, ProjectsService } from "./projects.service"; +import { + SavedSearchesService, + savedSearchResolvers, +} from "./saved-searches.service"; import { scriptResolvers, ScriptsService } from "./scripts.service"; import { SecurityService } from "./security.service"; import { @@ -23,6 +27,7 @@ import { AUDIO_EVENT_COMMENT, BOOKMARK, PROJECT, + SAVED_SEARCH, SCRIPT, SECURITY, SHALLOW_SITE, @@ -49,6 +54,7 @@ const services = [ AudioEventsService, BookmarksService, ProjectsService, + SavedSearchesService, ScriptsService, SecurityService, ShallowSitesService, @@ -65,6 +71,7 @@ const services = [ { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: BOOKMARK.token, useExisting: BookmarksService }, { provide: PROJECT.token, useExisting: ProjectsService }, + { provide: SAVED_SEARCH.token, useExisting: SavedSearchesService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, { provide: SHALLOW_SITE.token, useExisting: ShallowSitesService }, @@ -81,6 +88,7 @@ const resolvers = [ ...audioEventResolvers.providers, ...bookmarkResolvers.providers, ...projectResolvers.providers, + ...savedSearchResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, ...shallowSiteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 9d9186b40..e8fae0b93 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -5,6 +5,7 @@ import type { AudioEventCommentsService } from "./audio-event-comments.service"; import type { AudioEventsService } from "./audio-events.service"; import type { BookmarksService } from "./bookmarks.service"; import type { ProjectsService } from "./projects.service"; +import type { SavedSearchesService } from "./saved-searches.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; import type { ShallowSitesService, SitesService } from "./sites.service"; @@ -35,6 +36,9 @@ export const AUDIO_EVENT_COMMENT = new ServiceToken( ); export const BOOKMARK = new ServiceToken("BOOKMARKS_SERVICE"); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); +export const SAVED_SEARCH = new ServiceToken( + "SAVED_SEARCHES_SERVICE" +); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); export const SECURITY = new ServiceToken("SECURITY_SERVICE"); export const SHALLOW_SITE = new ServiceToken( diff --git a/src/app/services/baw-api/saved-searches.service.spec.ts b/src/app/services/baw-api/saved-searches.service.spec.ts new file mode 100644 index 000000000..843015004 --- /dev/null +++ b/src/app/services/baw-api/saved-searches.service.spec.ts @@ -0,0 +1,58 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { SavedSearch } from "@models/SavedSearch"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { SavedSearchesService } from "./saved-searches.service"; + +describe("SavedSearchesService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + SavedSearchesService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(SavedSearchesService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/saved_searches/"); + validateApiFilter( + "/saved_searches/filter" + ); + validateApiShow( + "/saved_searches/5", + 5, + new SavedSearch({ id: 5 }) + ); + validateApiCreate( + "/saved_searches/", + new SavedSearch({ id: 5 }) + ); + validateApiUpdate( + "/saved_searches/5", + new SavedSearch({ id: 5 }) + ); + validateApiDestroy( + "/saved_searches/5", + 5, + new SavedSearch({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/saved-searches.service.ts b/src/app/services/baw-api/saved-searches.service.ts new file mode 100644 index 000000000..c1665ca23 --- /dev/null +++ b/src/app/services/baw-api/saved-searches.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable, Injector } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { SavedSearch } from "@models/SavedSearch"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const savedSearchId: IdParamOptional = id; +const endpoint = stringTemplate`/saved_searches/${savedSearchId}${option}`; + +@Injectable() +export class SavedSearchesService extends StandardApi { + constructor( + http: HttpClient, + @Inject(API_ROOT) apiRoot: string, + injector: Injector + ) { + super(http, apiRoot, SavedSearch, injector); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: SavedSearch): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: SavedSearch): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const savedSearchResolvers = new Resolvers< + SavedSearch, + SavedSearchesService +>([SavedSearchesService], "savedSearchId").create("SavedSearch"); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index ecb1d5eb8..4630170c0 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -4,6 +4,7 @@ import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service"; import { AudioEventsService } from "@baw-api/audio-events.service"; import { BookmarksService } from "@baw-api/bookmarks.service"; +import { SavedSearchesService } from "@baw-api/saved-searches.service"; import { BehaviorSubject } from "rxjs"; import { API_CONFIG, @@ -83,6 +84,7 @@ export const testBawServices = [ { provide: AudioEventsService, useClass: MockStandardApiService }, { provide: BookmarksService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, + { provide: SavedSearchesService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, { provide: ShallowSitesService, useClass: MockStandardApiService }, From b944b59e5c57ee59aa3fc73d6a5abcbb615cd456 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 13:08:39 +1000 Subject: [PATCH 06/29] Created studies service --- src/app/services/baw-api/ServiceProviders.ts | 5 ++ src/app/services/baw-api/ServiceTokens.ts | 2 + .../services/baw-api/studies.service.spec.ts | 46 ++++++++++++++++ src/app/services/baw-api/studies.service.ts | 55 +++++++++++++++++++ src/app/test.helper.ts | 2 + 5 files changed, 110 insertions(+) create mode 100644 src/app/services/baw-api/studies.service.spec.ts create mode 100644 src/app/services/baw-api/studies.service.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 503b2a738..79811d971 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -32,6 +32,7 @@ import { SECURITY, SHALLOW_SITE, SITE, + STUDY, TAG, TAG_GROUP, USER, @@ -42,6 +43,7 @@ import { siteResolvers, SitesService, } from "./sites.service"; +import { StudiesService, studyResolvers } from "./studies.service"; import { tagGroupResolvers, TagGroupService } from "./tag-group.service"; import { tagResolvers, TagsService } from "./tags.service"; import { userResolvers, UserService } from "./user.service"; @@ -59,6 +61,7 @@ const services = [ SecurityService, ShallowSitesService, SitesService, + StudiesService, TagsService, TagGroupService, UserService, @@ -76,6 +79,7 @@ const services = [ { provide: SECURITY.token, useExisting: SecurityService }, { provide: SHALLOW_SITE.token, useExisting: ShallowSitesService }, { provide: SITE.token, useExisting: SitesService }, + { provide: STUDY.token, useExisting: StudiesService }, { provide: TAG.token, useExisting: TagsService }, { provide: TAG_GROUP.token, useExisting: TagGroupService }, { provide: USER.token, useExisting: UserService }, @@ -92,6 +96,7 @@ const resolvers = [ ...scriptResolvers.providers, ...siteResolvers.providers, ...shallowSiteResolvers.providers, + ...studyResolvers.providers, ...tagResolvers.providers, ...tagGroupResolvers.providers, ...userResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index e8fae0b93..8473429be 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -9,6 +9,7 @@ import type { SavedSearchesService } from "./saved-searches.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; import type { ShallowSitesService, SitesService } from "./sites.service"; +import type { StudiesService } from "./studies.service"; import type { TagGroupService } from "./tag-group.service"; import type { TagsService } from "./tags.service"; import type { UserService } from "./user.service"; @@ -45,6 +46,7 @@ export const SHALLOW_SITE = new ServiceToken( "SHALLOW_SITES_SERVICE" ); export const SITE = new ServiceToken("SITES_SERVICE"); +export const STUDY = new ServiceToken("STUDIES_SERVICE"); export const TAG = new ServiceToken("TAGS_SERVICE"); export const TAG_GROUP = new ServiceToken("TAG_GROUP_SERVICE"); export const USER = new ServiceToken("USER_SERVICE"); diff --git a/src/app/services/baw-api/studies.service.spec.ts b/src/app/services/baw-api/studies.service.spec.ts new file mode 100644 index 000000000..23aa9482d --- /dev/null +++ b/src/app/services/baw-api/studies.service.spec.ts @@ -0,0 +1,46 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Study } from "@models/Study"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { StudiesService } from "./studies.service"; + +describe("StudiesService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + StudiesService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(StudiesService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/studies/"); + validateApiFilter("/studies/filter"); + validateApiShow("/studies/5", 5, new Study({ id: 5 })); + validateApiCreate("/studies/", new Study({ id: 5 })); + validateApiUpdate("/studies/5", new Study({ id: 5 })); + validateApiDestroy( + "/studies/5", + 5, + new Study({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/studies.service.ts b/src/app/services/baw-api/studies.service.ts new file mode 100644 index 000000000..8f6a2e5b7 --- /dev/null +++ b/src/app/services/baw-api/studies.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable, Injector } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Study } from "@models/Study"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const studyId: IdParamOptional = id; +const endpoint = stringTemplate`/studies/${studyId}${option}`; + +@Injectable() +export class StudiesService extends StandardApi { + constructor( + http: HttpClient, + @Inject(API_ROOT) apiRoot: string, + injector: Injector + ) { + super(http, apiRoot, Study, injector); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: Study): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: Study): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const studyResolvers = new Resolvers( + [StudiesService], + "studyId" +).create("Study"); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index 4630170c0..5eb8eaf80 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -5,6 +5,7 @@ import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service import { AudioEventsService } from "@baw-api/audio-events.service"; import { BookmarksService } from "@baw-api/bookmarks.service"; import { SavedSearchesService } from "@baw-api/saved-searches.service"; +import { StudiesService } from "@baw-api/studies.service"; import { BehaviorSubject } from "rxjs"; import { API_CONFIG, @@ -88,6 +89,7 @@ export const testBawServices = [ { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, { provide: ShallowSitesService, useClass: MockStandardApiService }, + { provide: StudiesService, useClass: MockStandardApiService }, { provide: TagsService, useClass: MockStandardApiService }, { provide: TagGroupService, useClass: MockStandardApiService }, { provide: UserService, useClass: MockShowApiService }, From a944dde711b2e417f353c48a4bc72a04c07dfc07 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 13:43:50 +1000 Subject: [PATCH 07/29] Created question services --- src/app/models/Question.ts | 50 +++++++++++ src/app/services/baw-api/ServiceProviders.ts | 15 ++++ src/app/services/baw-api/ServiceTokens.ts | 8 ++ src/app/services/baw-api/api-common.ts | 36 ++++---- .../baw-api/questions-shallow.service.spec.ts | 56 ++++++++++++ .../baw-api/questions.service.spec.ts | 69 ++++++++++++++ src/app/services/baw-api/questions.service.ts | 90 +++++++++++++++++++ src/app/test.helper.ts | 6 ++ 8 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 src/app/models/Question.ts create mode 100644 src/app/services/baw-api/questions-shallow.service.spec.ts create mode 100644 src/app/services/baw-api/questions.service.spec.ts create mode 100644 src/app/services/baw-api/questions.service.ts diff --git a/src/app/models/Question.ts b/src/app/models/Question.ts new file mode 100644 index 000000000..effd5a8ba --- /dev/null +++ b/src/app/models/Question.ts @@ -0,0 +1,50 @@ +import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; +import { Observable } from "rxjs"; +import { + AbstractModel, + BawDateTime, + BawPersistAttr, + HasOne, +} from "./AbstractModel"; +import { User } from "./User"; + +export interface IQuestion { + id?: Id; + text?: Blob; + data?: Blob; + creatorId?: Id; + updaterId?: Id; + createdAt?: DateTimeTimezone | string; + updatedAt?: DateTimeTimezone | string; +} + +export class Question extends AbstractModel implements IQuestion { + public readonly kind: "Question" = "Question"; + @BawPersistAttr + public readonly id?: Id; + @BawPersistAttr + public readonly text?: Blob; + @BawPersistAttr + public readonly data?: Blob; + creatorId?: Id; + updaterId?: Id; + @BawDateTime() + createdAt?: DateTimeTimezone; + @BawDateTime() + updatedAt?: DateTimeTimezone; + + // Associations + @HasOne(ACCOUNT, (m: Question) => m.creatorId) + public creator?: Observable; + @HasOne(ACCOUNT, (m: Question) => m.updaterId) + public updater?: Observable; + + constructor(question: IQuestion) { + super(question); + } + + public get viewUrl(): string { + return "/BROKEN_LINK"; + } +} diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 79811d971..c337e3451 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -1,3 +1,4 @@ +import { Question } from "@models/Question"; import { AppConfigService } from "@services/app-config/app-config.service"; import { accountResolvers, AccountService } from "./account.service"; import { @@ -14,6 +15,12 @@ import { } from "./audio-events.service"; import { bookmarkResolvers, BookmarksService } from "./bookmarks.service"; import { projectResolvers, ProjectsService } from "./projects.service"; +import { + questionResolvers, + QuestionsService, + shallowQuestionResolvers, + ShallowQuestionsService, +} from "./questions.service"; import { SavedSearchesService, savedSearchResolvers, @@ -27,9 +34,11 @@ import { AUDIO_EVENT_COMMENT, BOOKMARK, PROJECT, + QUESTION, SAVED_SEARCH, SCRIPT, SECURITY, + SHALLOW_QUESTION, SHALLOW_SITE, SITE, STUDY, @@ -56,6 +65,8 @@ const services = [ AudioEventsService, BookmarksService, ProjectsService, + QuestionsService, + ShallowQuestionsService, SavedSearchesService, ScriptsService, SecurityService, @@ -74,6 +85,8 @@ const services = [ { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: BOOKMARK.token, useExisting: BookmarksService }, { provide: PROJECT.token, useExisting: ProjectsService }, + { provide: QUESTION.token, useExisting: Question }, + { provide: SHALLOW_QUESTION.token, useExisting: ShallowQuestionsService }, { provide: SAVED_SEARCH.token, useExisting: SavedSearchesService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, @@ -92,6 +105,8 @@ const resolvers = [ ...audioEventResolvers.providers, ...bookmarkResolvers.providers, ...projectResolvers.providers, + ...questionResolvers.providers, + ...shallowQuestionResolvers.providers, ...savedSearchResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 8473429be..6eeffa534 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -5,6 +5,10 @@ import type { AudioEventCommentsService } from "./audio-event-comments.service"; import type { AudioEventsService } from "./audio-events.service"; import type { BookmarksService } from "./bookmarks.service"; import type { ProjectsService } from "./projects.service"; +import type { + QuestionsService, + ShallowQuestionsService, +} from "./questions.service"; import type { SavedSearchesService } from "./saved-searches.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; @@ -37,6 +41,10 @@ export const AUDIO_EVENT_COMMENT = new ServiceToken( ); export const BOOKMARK = new ServiceToken("BOOKMARKS_SERVICE"); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); +export const QUESTION = new ServiceToken("QUESTIONS_SERVICE"); +export const SHALLOW_QUESTION = new ServiceToken( + "SHALLOW_QUESTIONS_SERVICE" +); export const SAVED_SEARCH = new ServiceToken( "SAVED_SEARCHES_SERVICE" ); diff --git a/src/app/services/baw-api/api-common.ts b/src/app/services/baw-api/api-common.ts index e9e844cc2..c12734f56 100644 --- a/src/app/services/baw-api/api-common.ts +++ b/src/app/services/baw-api/api-common.ts @@ -56,7 +56,7 @@ export const Filter: Filter = "filter"; /** * API List functionality */ -export interface ApiList { +export interface ApiList { /** * Get list of models * @param args URL parameter values @@ -67,7 +67,7 @@ export interface ApiList { /** * API Filter functionality */ -export interface ApiFilter { +export interface ApiFilter { /** * Get list of models, but filtered using the filter API * @param args URL parameter values @@ -80,8 +80,8 @@ export interface ApiFilter { */ export interface ApiShow< M extends AbstractModel, - P extends any[], - I extends IdOr + P extends any[] = [], + I extends IdOr = IdOr > { /** * Get individual model @@ -93,7 +93,7 @@ export interface ApiShow< /** * API Create functionality */ -export interface ApiCreate { +export interface ApiCreate { /** * Get individual model * @param args URL parameter values @@ -104,7 +104,7 @@ export interface ApiCreate { /** * API Update functionality */ -export interface ApiUpdate { +export interface ApiUpdate { /** * Get individual model * @param args URL parameter values @@ -116,8 +116,8 @@ export interface ApiUpdate { */ export interface ApiDestroy< M extends AbstractModel, - P extends any[], - I extends IdOr + P extends any[] = [], + I extends IdOr = IdOr > { /** * destroy individual model @@ -129,15 +129,15 @@ export interface ApiDestroy< /** * Api Class with all abilities enabled */ -export abstract class StandardApi +export abstract class StandardApi extends BawApiService implements ApiList, ApiFilter, - ApiShow>, + ApiShow, ApiCreate, ApiUpdate, - ApiDestroy> { + ApiDestroy { abstract list(...urlParameters: P): Observable; abstract filter(filters: Filters, ...urlParameters: P): Observable; abstract show(model: IdOr, ...urlParameters: P): Observable; @@ -152,14 +152,16 @@ export abstract class StandardApi /** * Api Class without the ability to update a model */ -export abstract class ImmutableApi - extends BawApiService +export abstract class ImmutableApi< + M extends AbstractModel, + P extends any[] = [] +> extends BawApiService implements ApiList, ApiFilter, - ApiShow>, + ApiShow, ApiCreate, - ApiDestroy> { + ApiDestroy { abstract list(...urlParameters: P): Observable; abstract filter(filters: Filters, ...urlParameters: P): Observable; abstract show(model: IdOr, ...urlParameters: P): Observable; @@ -170,9 +172,9 @@ export abstract class ImmutableApi /** * Api Class with only readable abilities enabled */ -export abstract class ReadonlyApi +export abstract class ReadonlyApi extends BawApiService - implements ApiList, ApiFilter, ApiShow> { + implements ApiList, ApiFilter, ApiShow { abstract list(...urlParameters: P): Observable; abstract filter(filters: Filters, ...urlParameters: P): Observable; abstract show(model: IdOr, ...urlParameters: P): Observable; diff --git a/src/app/services/baw-api/questions-shallow.service.spec.ts b/src/app/services/baw-api/questions-shallow.service.spec.ts new file mode 100644 index 000000000..4918ae323 --- /dev/null +++ b/src/app/services/baw-api/questions-shallow.service.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Question } from "@models/Question"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { ShallowQuestionsService } from "./questions.service"; + +describe("ShallowQuestionsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + ShallowQuestionsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(ShallowQuestionsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/questions/"); + validateApiFilter("/questions/filter"); + validateApiShow( + "/questions/5", + 5, + new Question({ id: 5 }) + ); + validateApiCreate( + "/questions/", + new Question({ id: 5 }) + ); + validateApiUpdate( + "/questions/5", + new Question({ id: 5 }) + ); + validateApiDestroy( + "/questions/5", + 5, + new Question({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/questions.service.spec.ts b/src/app/services/baw-api/questions.service.spec.ts new file mode 100644 index 000000000..14b8bb4f5 --- /dev/null +++ b/src/app/services/baw-api/questions.service.spec.ts @@ -0,0 +1,69 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Question } from "@models/Question"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { QuestionsService } from "./questions.service"; + +describe("QuestionsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + QuestionsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(QuestionsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList( + "/studies/5/questions/", + undefined, + 5 + ); + validateApiFilter( + "/studies/5/questions/filter", + undefined, + undefined, + 5 + ); + validateApiShow( + "/studies/5/questions/10", + 10, + new Question({ id: 10 }), + 5 + ); + validateApiCreate( + "/studies/5/questions/", + new Question({ id: 10 }), + 5 + ); + validateApiUpdate( + "/studies/5/questions/10", + new Question({ id: 10 }), + 5 + ); + validateApiDestroy( + "/studies/5/questions/10", + 10, + new Question({ id: 10 }), + 5 + ); +}); diff --git a/src/app/services/baw-api/questions.service.ts b/src/app/services/baw-api/questions.service.ts new file mode 100644 index 000000000..788053c4c --- /dev/null +++ b/src/app/services/baw-api/questions.service.ts @@ -0,0 +1,90 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Question } from "@models/Question"; +import { Study } from "@models/Study"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParam, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const studyId: IdParam = id; +const questionId: IdParamOptional = id; +const endpoint = stringTemplate`/studies/${studyId}/questions/${questionId}${option}`; +const endpointShallow = stringTemplate`/questions/${questionId}${option}`; + +@Injectable() +export class QuestionsService extends StandardApi]> { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, Question); + } + + list(study: IdOr): Observable { + return this.apiList(endpoint(study, Empty, Empty)); + } + filter(filters: Filters, study: IdOr): Observable { + return this.apiFilter(endpoint(study, Empty, Filter), filters); + } + show(model: IdOr, study: IdOr): Observable { + return this.apiShow(endpoint(study, model, Empty)); + } + create(model: Question, study: IdOr): Observable { + return this.apiCreate(endpoint(study, Empty, Empty), model); + } + update(model: Question, study: IdOr): Observable { + return this.apiUpdate(endpoint(study, model, Empty), model); + } + destroy( + model: IdOr, + study: IdOr + ): Observable { + return this.apiDestroy(endpoint(study, model, Empty)); + } +} + +@Injectable() +export class ShallowQuestionsService extends StandardApi { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, Question); + } + + list(): Observable { + return this.apiList(endpointShallow(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpointShallow(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpointShallow(model, Empty)); + } + create(model: Question): Observable { + return this.apiCreate(endpointShallow(Empty, Empty), model); + } + update(model: Question): Observable { + return this.apiUpdate(endpointShallow(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpointShallow(model, Empty)); + } +} + +export const questionResolvers = new Resolvers( + [QuestionsService], + "questionId", + ["studyId"] +).create("Question"); + +export const shallowQuestionResolvers = new Resolvers< + Question, + ShallowQuestionsService +>([ShallowQuestionsService], "questionId").create("ShallowQuestion"); diff --git a/src/app/test.helper.ts b/src/app/test.helper.ts index 5eb8eaf80..11c95a85a 100644 --- a/src/app/test.helper.ts +++ b/src/app/test.helper.ts @@ -4,6 +4,10 @@ import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service"; import { AudioEventsService } from "@baw-api/audio-events.service"; import { BookmarksService } from "@baw-api/bookmarks.service"; +import { + QuestionsService, + ShallowQuestionsService, +} from "@baw-api/questions.service"; import { SavedSearchesService } from "@baw-api/saved-searches.service"; import { StudiesService } from "@baw-api/studies.service"; import { BehaviorSubject } from "rxjs"; @@ -85,6 +89,8 @@ export const testBawServices = [ { provide: AudioEventsService, useClass: MockStandardApiService }, { provide: BookmarksService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, + { provide: QuestionsService, useClass: MockStandardApiService }, + { provide: ShallowQuestionsService, useClass: MockStandardApiService }, { provide: SavedSearchesService, useClass: MockStandardApiService }, { provide: ScriptsService, useClass: MockStandardApiService }, { provide: SitesService, useClass: MockStandardApiService }, From 5fc52530f80735377f0b6587fca2d67ec25f257d Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 14:19:22 +1000 Subject: [PATCH 08/29] Created response services --- src/app/models/Response.ts | 50 +++++++++++ src/app/services/baw-api/ServiceProviders.ts | 17 +++- src/app/services/baw-api/ServiceTokens.ts | 8 ++ .../baw-api/audio-event-comments.service.ts | 18 ---- .../baw-api/responses-shallow.service.spec.ts | 57 ++++++++++++ .../baw-api/responses.service.spec.ts | 69 ++++++++++++++ src/app/services/baw-api/responses.service.ts | 90 +++++++++++++++++++ 7 files changed, 289 insertions(+), 20 deletions(-) create mode 100644 src/app/models/Response.ts create mode 100644 src/app/services/baw-api/responses-shallow.service.spec.ts create mode 100644 src/app/services/baw-api/responses.service.spec.ts create mode 100644 src/app/services/baw-api/responses.service.ts diff --git a/src/app/models/Response.ts b/src/app/models/Response.ts new file mode 100644 index 000000000..471c06034 --- /dev/null +++ b/src/app/models/Response.ts @@ -0,0 +1,50 @@ +import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; +import { Observable } from "rxjs"; +import { + AbstractModel, + BawDateTime, + BawPersistAttr, + HasOne, +} from "./AbstractModel"; +import { User } from "./User"; + +export interface IResponse { + id?: Id; + data?: Blob; + datasetItemId?: Id; + questionId?: Id; + studyId?: Id; + creatorId?: Id; + createdAt?: DateTimeTimezone | string; +} + +export class Response extends AbstractModel implements IResponse { + public readonly kind: "Answer" = "Answer"; + @BawPersistAttr + public readonly id?: Id; + @BawPersistAttr + public readonly data?: Blob; + @BawPersistAttr + public readonly datasetItemId?: Id; + @BawPersistAttr + public readonly questionId?: Id; + @BawPersistAttr + public readonly studyId?: Id; + public readonly creatorId?: Id; + @BawDateTime() + public readonly createdAt?: DateTimeTimezone | string; + + // Associations + // TODO Add DatasetItem, Question, and Study associations + @HasOne(ACCOUNT, (m: Response) => m.creatorId) + public creator?: Observable; + + constructor(question: IResponse) { + super(question); + } + + public get viewUrl(): string { + return "/BROKEN_LINK"; + } +} diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index c337e3451..06ce8d1d7 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -1,4 +1,3 @@ -import { Question } from "@models/Question"; import { AppConfigService } from "@services/app-config/app-config.service"; import { accountResolvers, AccountService } from "./account.service"; import { @@ -21,6 +20,12 @@ import { shallowQuestionResolvers, ShallowQuestionsService, } from "./questions.service"; +import { + responseResolvers, + ResponsesService, + shallowResponseResolvers, + ShallowResponsesService, +} from "./responses.service"; import { SavedSearchesService, savedSearchResolvers, @@ -35,10 +40,12 @@ import { BOOKMARK, PROJECT, QUESTION, + RESPONSE, SAVED_SEARCH, SCRIPT, SECURITY, SHALLOW_QUESTION, + SHALLOW_RESPONSE, SHALLOW_SITE, SITE, STUDY, @@ -67,6 +74,8 @@ const services = [ ProjectsService, QuestionsService, ShallowQuestionsService, + ResponsesService, + ShallowResponsesService, SavedSearchesService, ScriptsService, SecurityService, @@ -85,8 +94,10 @@ const services = [ { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: BOOKMARK.token, useExisting: BookmarksService }, { provide: PROJECT.token, useExisting: ProjectsService }, - { provide: QUESTION.token, useExisting: Question }, + { provide: QUESTION.token, useExisting: QuestionsService }, { provide: SHALLOW_QUESTION.token, useExisting: ShallowQuestionsService }, + { provide: RESPONSE.token, useExisting: ResponsesService }, + { provide: SHALLOW_RESPONSE.token, useExisting: ShallowResponsesService }, { provide: SAVED_SEARCH.token, useExisting: SavedSearchesService }, { provide: SCRIPT.token, useExisting: ScriptsService }, { provide: SECURITY.token, useExisting: SecurityService }, @@ -107,6 +118,8 @@ const resolvers = [ ...projectResolvers.providers, ...questionResolvers.providers, ...shallowQuestionResolvers.providers, + ...responseResolvers.providers, + ...shallowResponseResolvers.providers, ...savedSearchResolvers.providers, ...scriptResolvers.providers, ...siteResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 6eeffa534..3d945fe38 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -9,6 +9,10 @@ import type { QuestionsService, ShallowQuestionsService, } from "./questions.service"; +import type { + ResponsesService, + ShallowResponsesService, +} from "./responses.service"; import type { SavedSearchesService } from "./saved-searches.service"; import type { ScriptsService } from "./scripts.service"; import type { SecurityService } from "./security.service"; @@ -45,6 +49,10 @@ export const QUESTION = new ServiceToken("QUESTIONS_SERVICE"); export const SHALLOW_QUESTION = new ServiceToken( "SHALLOW_QUESTIONS_SERVICE" ); +export const RESPONSE = new ServiceToken("RESPONSES_SERVICE"); +export const SHALLOW_RESPONSE = new ServiceToken( + "SHALLOW_RESPONSES_SERVICE" +); export const SAVED_SEARCH = new ServiceToken( "SAVED_SEARCHES_SERVICE" ); diff --git a/src/app/services/baw-api/audio-event-comments.service.ts b/src/app/services/baw-api/audio-event-comments.service.ts index 8fb38c6c7..0c6b887bd 100644 --- a/src/app/services/baw-api/audio-event-comments.service.ts +++ b/src/app/services/baw-api/audio-event-comments.service.ts @@ -34,54 +34,36 @@ export class AudioEventCommentsService extends StandardApi< list(analysis: IdOr): Observable { return this.apiList(endpoint(analysis, Empty, Empty)); } - apiList(arg0: any): any { - throw new Error("Method not implemented."); - } filter( filters: Filters, analysis: IdOr ): Observable { return this.apiFilter(endpoint(analysis, Empty, Filter), filters); } - apiFilter(arg0: any, filters: any): any { - throw new Error("Method not implemented."); - } show( model: IdOr, analysis: IdOr ): Observable { return this.apiShow(endpoint(analysis, model, Empty)); } - apiShow(arg0: any): any { - throw new Error("Method not implemented."); - } create( model: AudioEventComment, analysis: IdOr ): Observable { return this.apiCreate(endpoint(analysis, Empty, Empty), model); } - apiCreate(arg0: any, model: AudioEventComment): any { - throw new Error("Method not implemented."); - } update( model: AudioEventComment, analysis: IdOr ): Observable { return this.apiUpdate(endpoint(analysis, model, Empty), model); } - apiUpdate(arg0: any, model: AudioEventComment): any { - throw new Error("Method not implemented."); - } destroy( model: IdOr, analysis: IdOr ): Observable { return this.apiDestroy(endpoint(analysis, model, Empty)); } - apiDestroy(arg0: any): any { - throw new Error("Method not implemented."); - } } export const audioEventCommentResolvers = new Resolvers< diff --git a/src/app/services/baw-api/responses-shallow.service.spec.ts b/src/app/services/baw-api/responses-shallow.service.spec.ts new file mode 100644 index 000000000..1fedca5f9 --- /dev/null +++ b/src/app/services/baw-api/responses-shallow.service.spec.ts @@ -0,0 +1,57 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Response } from "@models/Response"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { ShallowQuestionsService } from "./questions.service"; +import { ShallowResponsesService } from "./responses.service"; + +describe("ShallowResponsesService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + ShallowResponsesService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(ShallowResponsesService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/responses/"); + validateApiFilter("/responses/filter"); + validateApiShow( + "/responses/5", + 5, + new Response({ id: 5 }) + ); + validateApiCreate( + "/responses/", + new Response({ id: 5 }) + ); + validateApiUpdate( + "/responses/5", + new Response({ id: 5 }) + ); + validateApiDestroy( + "/responses/5", + 5, + new Response({ id: 5 }) + ); +}); diff --git a/src/app/services/baw-api/responses.service.spec.ts b/src/app/services/baw-api/responses.service.spec.ts new file mode 100644 index 000000000..1509219c2 --- /dev/null +++ b/src/app/services/baw-api/responses.service.spec.ts @@ -0,0 +1,69 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Response } from "@models/Response"; +import { testAppInitializer } from "src/app/test.helper"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "./api-common.helper"; +import { BawApiService } from "./baw-api.service"; +import { MockBawApiService } from "./mock/baseApiMock.service"; +import { ResponsesService } from "./responses.service"; + +describe("ResponsesService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + ResponsesService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(ResponsesService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList( + "/studies/5/responses/", + undefined, + 5 + ); + validateApiFilter( + "/studies/5/responses/filter", + undefined, + undefined, + 5 + ); + validateApiShow( + "/studies/5/responses/10", + 10, + new Response({ id: 10 }), + 5 + ); + validateApiCreate( + "/studies/5/responses/", + new Response({ id: 10 }), + 5 + ); + validateApiUpdate( + "/studies/5/responses/10", + new Response({ id: 10 }), + 5 + ); + validateApiDestroy( + "/studies/5/responses/10", + 10, + new Response({ id: 10 }), + 5 + ); +}); diff --git a/src/app/services/baw-api/responses.service.ts b/src/app/services/baw-api/responses.service.ts new file mode 100644 index 000000000..ff1c9dea0 --- /dev/null +++ b/src/app/services/baw-api/responses.service.ts @@ -0,0 +1,90 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Response } from "@models/Response"; +import { Study } from "@models/Study"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParam, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const studyId: IdParam = id; +const responseId: IdParamOptional = id; +const endpoint = stringTemplate`/studies/${studyId}/responses/${responseId}${option}`; +const endpointShallow = stringTemplate`/responses/${responseId}${option}`; + +@Injectable() +export class ResponsesService extends StandardApi]> { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, Response); + } + + list(study: IdOr): Observable { + return this.apiList(endpoint(study, Empty, Empty)); + } + filter(filters: Filters, study: IdOr): Observable { + return this.apiFilter(endpoint(study, Empty, Filter), filters); + } + show(model: IdOr, study: IdOr): Observable { + return this.apiShow(endpoint(study, model, Empty)); + } + create(model: Response, study: IdOr): Observable { + return this.apiCreate(endpoint(study, Empty, Empty), model); + } + update(model: Response, study: IdOr): Observable { + return this.apiUpdate(endpoint(study, model, Empty), model); + } + destroy( + model: IdOr, + study: IdOr + ): Observable { + return this.apiDestroy(endpoint(study, model, Empty)); + } +} + +@Injectable() +export class ShallowResponsesService extends StandardApi { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, Response); + } + + list(): Observable { + return this.apiList(endpointShallow(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpointShallow(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpointShallow(model, Empty)); + } + create(model: Response): Observable { + return this.apiCreate(endpointShallow(Empty, Empty), model); + } + update(model: Response): Observable { + return this.apiUpdate(endpointShallow(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpointShallow(model, Empty)); + } +} + +export const responseResolvers = new Resolvers( + [ResponsesService], + "responseId", + ["studyId"] +).create("Response"); + +export const shallowResponseResolvers = new Resolvers< + Response, + ShallowResponsesService +>([ShallowResponsesService], "responseId").create("ShallowResponse"); From 3f3c9a3a616300e9613ecd80bec5370f69c39bdf Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 15:14:31 +1000 Subject: [PATCH 09/29] Extracted service tests to folder --- src/app/models/AbstractModel.spec.ts | 5 +++- .../{ => tests}/account.service.spec.ts | 10 ++++---- .../{ => tests}/analysis-jobs.service.spec.ts | 10 ++++---- .../api.interceptor.service.spec.ts | 9 ++++--- .../audio-event-comments.service.spec.ts | 10 ++++---- .../{ => tests}/audio-events.service.spec.ts | 10 ++++---- .../{ => tests}/baw-api.service.spec.ts | 25 +++++++++++-------- .../{ => tests}/bookmarks.service.spec.ts | 10 ++++---- .../{ => tests}/projects.service.spec.ts | 10 ++++---- .../questions-shallow.service.spec.ts | 10 ++++---- .../{ => tests}/questions.service.spec.ts | 10 ++++---- .../{ => tests}/resolver-common.spec.ts | 4 +-- .../responses-shallow.service.spec.ts | 11 ++++---- .../{ => tests}/responses.service.spec.ts | 10 ++++---- .../saved-searches.service.spec.ts | 10 ++++---- .../{ => tests}/scripts.service.spec.ts | 10 ++++---- .../{ => tests}/security.service.spec.ts | 11 +++++--- .../{ => tests}/sites-shallow.service.spec.ts | 10 ++++---- .../baw-api/{ => tests}/sites.service.spec.ts | 10 ++++---- .../{ => tests}/studies.service.spec.ts | 10 ++++---- .../{ => tests}/tag-group.service.spec.ts | 10 ++++---- .../baw-api/{ => tests}/tags.service.spec.ts | 10 ++++---- .../baw-api/{ => tests}/user.service.spec.ts | 8 +++--- 23 files changed, 122 insertions(+), 111 deletions(-) rename src/app/services/baw-api/{ => tests}/account.service.spec.ts (85%) rename src/app/services/baw-api/{ => tests}/analysis-jobs.service.spec.ts (83%) rename src/app/services/baw-api/{ => tests}/api.interceptor.service.spec.ts (98%) rename src/app/services/baw-api/{ => tests}/audio-event-comments.service.spec.ts (85%) rename src/app/services/baw-api/{ => tests}/audio-events.service.spec.ts (85%) rename src/app/services/baw-api/{ => tests}/baw-api.service.spec.ts (98%) rename src/app/services/baw-api/{ => tests}/bookmarks.service.spec.ts (83%) rename src/app/services/baw-api/{ => tests}/projects.service.spec.ts (86%) rename src/app/services/baw-api/{ => tests}/questions-shallow.service.spec.ts (83%) rename src/app/services/baw-api/{ => tests}/questions.service.spec.ts (84%) rename src/app/services/baw-api/{ => tests}/resolver-common.spec.ts (95%) rename src/app/services/baw-api/{ => tests}/responses-shallow.service.spec.ts (81%) rename src/app/services/baw-api/{ => tests}/responses.service.spec.ts (84%) rename src/app/services/baw-api/{ => tests}/saved-searches.service.spec.ts (83%) rename src/app/services/baw-api/{ => tests}/scripts.service.spec.ts (84%) rename src/app/services/baw-api/{ => tests}/security.service.spec.ts (96%) rename src/app/services/baw-api/{ => tests}/sites-shallow.service.spec.ts (86%) rename src/app/services/baw-api/{ => tests}/sites.service.spec.ts (87%) rename src/app/services/baw-api/{ => tests}/studies.service.spec.ts (82%) rename src/app/services/baw-api/{ => tests}/tag-group.service.spec.ts (86%) rename src/app/services/baw-api/{ => tests}/tags.service.spec.ts (86%) rename src/app/services/baw-api/{ => tests}/user.service.spec.ts (77%) diff --git a/src/app/models/AbstractModel.spec.ts b/src/app/models/AbstractModel.spec.ts index b2e67e67c..17e6de956 100644 --- a/src/app/models/AbstractModel.spec.ts +++ b/src/app/models/AbstractModel.spec.ts @@ -2,12 +2,15 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { Injector } from "@angular/core"; import { async, fakeAsync, TestBed, tick } from "@angular/core/testing"; import { ApiErrorDetails } from "@baw-api/api.interceptor.service"; -import { shouldNotFail, shouldNotSucceed } from "@baw-api/baw-api.service.spec"; import { MockModel as ChildModel } from "@baw-api/mock/baseApiMock.service"; import { MOCK, MockStandardApiService, } from "@baw-api/mock/standardApiMock.service"; +import { + shouldNotFail, + shouldNotSucceed, +} from "@baw-api/tests/baw-api.service.spec"; import { Id, Ids } from "@interfaces/apiInterfaces"; import { DateTime, Duration } from "luxon"; import { BehaviorSubject, Observable, Subject } from "rxjs"; diff --git a/src/app/services/baw-api/account.service.spec.ts b/src/app/services/baw-api/tests/account.service.spec.ts similarity index 85% rename from src/app/services/baw-api/account.service.spec.ts rename to src/app/services/baw-api/tests/account.service.spec.ts index aa38b6c83..0b38b0e7b 100644 --- a/src/app/services/baw-api/account.service.spec.ts +++ b/src/app/services/baw-api/tests/account.service.spec.ts @@ -1,8 +1,10 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; +import { AccountService } from "@baw-api/account.service"; +import { BawApiService } from "@baw-api/baw-api.service"; +import { MockBawApiService } from "@baw-api/mock/baseApiMock.service"; import { User } from "@models/User"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +12,8 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { AccountService } from "./account.service"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; describe("AccountService", () => { beforeEach(function () { diff --git a/src/app/services/baw-api/analysis-jobs.service.spec.ts b/src/app/services/baw-api/tests/analysis-jobs.service.spec.ts similarity index 83% rename from src/app/services/baw-api/analysis-jobs.service.spec.ts rename to src/app/services/baw-api/tests/analysis-jobs.service.spec.ts index 4c59e455a..47537a0ff 100644 --- a/src/app/services/baw-api/analysis-jobs.service.spec.ts +++ b/src/app/services/baw-api/tests/analysis-jobs.service.spec.ts @@ -2,8 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { AnalysisJob } from "@models/AnalysisJob"; -import { testAppInitializer } from "src/app/test.helper"; -import { AnalysisJobsService } from "./analysis-jobs.service"; import { validateApiCreate, validateApiDestroy, @@ -11,9 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { AnalysisJobsService } from "../analysis-jobs.service"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; describe("AnalysisJobsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/api.interceptor.service.spec.ts b/src/app/services/baw-api/tests/api.interceptor.service.spec.ts similarity index 98% rename from src/app/services/baw-api/api.interceptor.service.spec.ts rename to src/app/services/baw-api/tests/api.interceptor.service.spec.ts index 1fb272c95..93fe1ef2c 100644 --- a/src/app/services/baw-api/api.interceptor.service.spec.ts +++ b/src/app/services/baw-api/tests/api.interceptor.service.spec.ts @@ -9,11 +9,14 @@ import { HttpTestingController, } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; +import { + ApiErrorDetails, + BawApiInterceptor, +} from "@baw-api/api.interceptor.service"; +import { SecurityService } from "@baw-api/security.service"; import { SessionUser } from "@models/User"; +import { AppConfigService } from "@services/app-config/app-config.service"; import { testBawServices } from "src/app/test/helpers/testbed"; -import { AppConfigService } from "../app-config/app-config.service"; -import { ApiErrorDetails, BawApiInterceptor } from "./api.interceptor.service"; -import { SecurityService } from "./security.service"; describe("BawApiInterceptor", () => { let api: SecurityService; diff --git a/src/app/services/baw-api/audio-event-comments.service.spec.ts b/src/app/services/baw-api/tests/audio-event-comments.service.spec.ts similarity index 85% rename from src/app/services/baw-api/audio-event-comments.service.spec.ts rename to src/app/services/baw-api/tests/audio-event-comments.service.spec.ts index 2bc4340df..cee628f67 100644 --- a/src/app/services/baw-api/audio-event-comments.service.spec.ts +++ b/src/app/services/baw-api/tests/audio-event-comments.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { AudioEventComment } from "@models/AudioEventComment"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { AudioEventCommentsService } from "./audio-event-comments.service"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { AudioEventCommentsService } from "../audio-event-comments.service"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; describe("AudioEventCommentsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/audio-events.service.spec.ts b/src/app/services/baw-api/tests/audio-events.service.spec.ts similarity index 85% rename from src/app/services/baw-api/audio-events.service.spec.ts rename to src/app/services/baw-api/tests/audio-events.service.spec.ts index 3815fb516..7c5e83372 100644 --- a/src/app/services/baw-api/audio-events.service.spec.ts +++ b/src/app/services/baw-api/tests/audio-events.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { AudioEvent } from "@models/AudioEvent"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { AudioEventsService } from "./audio-events.service"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { AudioEventsService } from "../audio-events.service"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; describe("AudioEventsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/baw-api.service.spec.ts b/src/app/services/baw-api/tests/baw-api.service.spec.ts similarity index 98% rename from src/app/services/baw-api/baw-api.service.spec.ts rename to src/app/services/baw-api/tests/baw-api.service.spec.ts index fec8470eb..6473e605f 100644 --- a/src/app/services/baw-api/baw-api.service.spec.ts +++ b/src/app/services/baw-api/tests/baw-api.service.spec.ts @@ -6,22 +6,25 @@ import { } from "@angular/common/http/testing"; import { Injector } from "@angular/core"; import { TestBed } from "@angular/core/testing"; -import { AbstractModel } from "@models/AbstractModel"; -import { SessionUser } from "@models/User"; -import { BehaviorSubject, Subject } from "rxjs"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; -import { AppConfigService } from "../app-config/app-config.service"; -import { ApiErrorDetails, BawApiInterceptor } from "./api.interceptor.service"; +import { + ApiErrorDetails, + BawApiInterceptor, +} from "@baw-api/api.interceptor.service"; import { ApiResponse, BawApiService, Meta, STUB_MODEL_BUILDER, -} from "./baw-api.service"; -import { MockSecurityService } from "./mock/securityMock.service"; -import { MockShowApiService } from "./mock/showApiMock.service"; -import { SecurityService } from "./security.service"; -import { UserService } from "./user.service"; +} from "@baw-api/baw-api.service"; +import { MockSecurityService } from "@baw-api/mock/securityMock.service"; +import { MockShowApiService } from "@baw-api/mock/showApiMock.service"; +import { SecurityService } from "@baw-api/security.service"; +import { UserService } from "@baw-api/user.service"; +import { AbstractModel } from "@models/AbstractModel"; +import { SessionUser } from "@models/User"; +import { AppConfigService } from "@services/app-config/app-config.service"; +import { BehaviorSubject, Subject } from "rxjs"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; export const shouldNotSucceed = () => { fail("Service should not produce a data output"); diff --git a/src/app/services/baw-api/bookmarks.service.spec.ts b/src/app/services/baw-api/tests/bookmarks.service.spec.ts similarity index 83% rename from src/app/services/baw-api/bookmarks.service.spec.ts rename to src/app/services/baw-api/tests/bookmarks.service.spec.ts index 78013e602..edc500435 100644 --- a/src/app/services/baw-api/bookmarks.service.spec.ts +++ b/src/app/services/baw-api/tests/bookmarks.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Bookmark } from "@models/Bookmark"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { BookmarksService } from "./bookmarks.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { BookmarksService } from "../bookmarks.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; describe("BookmarksService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/projects.service.spec.ts b/src/app/services/baw-api/tests/projects.service.spec.ts similarity index 86% rename from src/app/services/baw-api/projects.service.spec.ts rename to src/app/services/baw-api/tests/projects.service.spec.ts index 40b74dc66..31bf8964d 100644 --- a/src/app/services/baw-api/projects.service.spec.ts +++ b/src/app/services/baw-api/tests/projects.service.spec.ts @@ -1,8 +1,10 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; +import { BawApiService } from "@baw-api/baw-api.service"; +import { MockBawApiService } from "@baw-api/mock/baseApiMock.service"; +import { ProjectsService } from "@baw-api/projects.service"; import { Project } from "@models/Project"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +12,8 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ProjectsService } from "./projects.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; describe("ProjectsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/questions-shallow.service.spec.ts b/src/app/services/baw-api/tests/questions-shallow.service.spec.ts similarity index 83% rename from src/app/services/baw-api/questions-shallow.service.spec.ts rename to src/app/services/baw-api/tests/questions-shallow.service.spec.ts index 4918ae323..825091862 100644 --- a/src/app/services/baw-api/questions-shallow.service.spec.ts +++ b/src/app/services/baw-api/tests/questions-shallow.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Question } from "@models/Question"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ShallowQuestionsService } from "./questions.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { ShallowQuestionsService } from "../questions.service"; describe("ShallowQuestionsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/questions.service.spec.ts b/src/app/services/baw-api/tests/questions.service.spec.ts similarity index 84% rename from src/app/services/baw-api/questions.service.spec.ts rename to src/app/services/baw-api/tests/questions.service.spec.ts index 14b8bb4f5..ac109a922 100644 --- a/src/app/services/baw-api/questions.service.spec.ts +++ b/src/app/services/baw-api/tests/questions.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Question } from "@models/Question"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { QuestionsService } from "./questions.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { QuestionsService } from "../questions.service"; describe("QuestionsService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/resolver-common.spec.ts b/src/app/services/baw-api/tests/resolver-common.spec.ts similarity index 95% rename from src/app/services/baw-api/resolver-common.spec.ts rename to src/app/services/baw-api/tests/resolver-common.spec.ts index cf92a3caf..d0633e5a4 100644 --- a/src/app/services/baw-api/resolver-common.spec.ts +++ b/src/app/services/baw-api/tests/resolver-common.spec.ts @@ -1,6 +1,6 @@ import { AbstractModel } from "@models/AbstractModel"; -import { ApiErrorDetails } from "./api.interceptor.service"; -import { retrieveResolvers } from "./resolver-common"; +import { ApiErrorDetails } from "../api.interceptor.service"; +import { retrieveResolvers } from "../resolver-common"; class MockModel extends AbstractModel { public get viewUrl(): string { diff --git a/src/app/services/baw-api/responses-shallow.service.spec.ts b/src/app/services/baw-api/tests/responses-shallow.service.spec.ts similarity index 81% rename from src/app/services/baw-api/responses-shallow.service.spec.ts rename to src/app/services/baw-api/tests/responses-shallow.service.spec.ts index 1fedca5f9..32fdf4c48 100644 --- a/src/app/services/baw-api/responses-shallow.service.spec.ts +++ b/src/app/services/baw-api/tests/responses-shallow.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Response } from "@models/Response"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,11 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ShallowQuestionsService } from "./questions.service"; -import { ShallowResponsesService } from "./responses.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { ShallowResponsesService } from "../responses.service"; describe("ShallowResponsesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/responses.service.spec.ts b/src/app/services/baw-api/tests/responses.service.spec.ts similarity index 84% rename from src/app/services/baw-api/responses.service.spec.ts rename to src/app/services/baw-api/tests/responses.service.spec.ts index 1509219c2..ab58af96a 100644 --- a/src/app/services/baw-api/responses.service.spec.ts +++ b/src/app/services/baw-api/tests/responses.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Response } from "@models/Response"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ResponsesService } from "./responses.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { ResponsesService } from "../responses.service"; describe("ResponsesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/saved-searches.service.spec.ts b/src/app/services/baw-api/tests/saved-searches.service.spec.ts similarity index 83% rename from src/app/services/baw-api/saved-searches.service.spec.ts rename to src/app/services/baw-api/tests/saved-searches.service.spec.ts index 843015004..8db0cdbb1 100644 --- a/src/app/services/baw-api/saved-searches.service.spec.ts +++ b/src/app/services/baw-api/tests/saved-searches.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { SavedSearch } from "@models/SavedSearch"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { SavedSearchesService } from "./saved-searches.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { SavedSearchesService } from "../saved-searches.service"; describe("SavedSearchesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/scripts.service.spec.ts b/src/app/services/baw-api/tests/scripts.service.spec.ts similarity index 84% rename from src/app/services/baw-api/scripts.service.spec.ts rename to src/app/services/baw-api/tests/scripts.service.spec.ts index 161d39c65..72f732141 100644 --- a/src/app/services/baw-api/scripts.service.spec.ts +++ b/src/app/services/baw-api/tests/scripts.service.spec.ts @@ -1,18 +1,18 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { async, TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; +import { BawApiService } from "@baw-api/baw-api.service"; +import { MockBawApiService } from "@baw-api/mock/baseApiMock.service"; +import { ScriptsService } from "@baw-api/scripts.service"; import { Script } from "@models/Script"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, validateApiFilter, validateApiList, validateApiShow, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ScriptsService } from "./scripts.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; describe("ScriptsService", function () { beforeEach(async(function () { diff --git a/src/app/services/baw-api/security.service.spec.ts b/src/app/services/baw-api/tests/security.service.spec.ts similarity index 96% rename from src/app/services/baw-api/security.service.spec.ts rename to src/app/services/baw-api/tests/security.service.spec.ts index c5b200a3c..5b7f06f66 100644 --- a/src/app/services/baw-api/security.service.spec.ts +++ b/src/app/services/baw-api/tests/security.service.spec.ts @@ -4,19 +4,22 @@ import { HttpTestingController, } from "@angular/common/http/testing"; import { fakeAsync, TestBed } from "@angular/core/testing"; +import { + ApiErrorDetails, + BawApiInterceptor, +} from "@baw-api/api.interceptor.service"; import { SessionUser, User } from "@models/User"; import { BehaviorSubject, Subject } from "rxjs"; import { testAppInitializer } from "src/app/test/helpers/testbed"; -import { ApiErrorDetails, BawApiInterceptor } from "./api.interceptor.service"; +import { MockShowApiService } from "../mock/showApiMock.service"; +import { LoginDetails, SecurityService } from "../security.service"; +import { UserService } from "../user.service"; import { apiErrorDetails, shouldNotComplete, shouldNotFail, shouldNotSucceed, } from "./baw-api.service.spec"; -import { MockShowApiService } from "./mock/showApiMock.service"; -import { LoginDetails, SecurityService } from "./security.service"; -import { UserService } from "./user.service"; describe("SecurityService", () => { let service: SecurityService; diff --git a/src/app/services/baw-api/sites-shallow.service.spec.ts b/src/app/services/baw-api/tests/sites-shallow.service.spec.ts similarity index 86% rename from src/app/services/baw-api/sites-shallow.service.spec.ts rename to src/app/services/baw-api/tests/sites-shallow.service.spec.ts index 1d312f8b7..8ea2b01d5 100644 --- a/src/app/services/baw-api/sites-shallow.service.spec.ts +++ b/src/app/services/baw-api/tests/sites-shallow.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Site } from "@models/Site"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { ShallowSitesService } from "./sites.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { ShallowSitesService } from "../sites.service"; xdescribe("ShallowSitesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/sites.service.spec.ts b/src/app/services/baw-api/tests/sites.service.spec.ts similarity index 87% rename from src/app/services/baw-api/sites.service.spec.ts rename to src/app/services/baw-api/tests/sites.service.spec.ts index e5b27bbc7..da719d40d 100644 --- a/src/app/services/baw-api/sites.service.spec.ts +++ b/src/app/services/baw-api/tests/sites.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Site } from "@models/Site"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { SitesService } from "./sites.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { SitesService } from "../sites.service"; describe("SitesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/studies.service.spec.ts b/src/app/services/baw-api/tests/studies.service.spec.ts similarity index 82% rename from src/app/services/baw-api/studies.service.spec.ts rename to src/app/services/baw-api/tests/studies.service.spec.ts index 23aa9482d..da6cb2cd9 100644 --- a/src/app/services/baw-api/studies.service.spec.ts +++ b/src/app/services/baw-api/tests/studies.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Study } from "@models/Study"; -import { testAppInitializer } from "src/app/test.helper"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "./api-common.helper"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { StudiesService } from "./studies.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { StudiesService } from "../studies.service"; describe("StudiesService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/tag-group.service.spec.ts b/src/app/services/baw-api/tests/tag-group.service.spec.ts similarity index 86% rename from src/app/services/baw-api/tag-group.service.spec.ts rename to src/app/services/baw-api/tests/tag-group.service.spec.ts index b3664c638..80bb33e5b 100644 --- a/src/app/services/baw-api/tag-group.service.spec.ts +++ b/src/app/services/baw-api/tests/tag-group.service.spec.ts @@ -1,8 +1,10 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; +import { BawApiService } from "@baw-api/baw-api.service"; +import { MockBawApiService } from "@baw-api/mock/baseApiMock.service"; +import { TagGroupService } from "@baw-api/tag-group.service"; import { TagGroup } from "@models/TagGroup"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +12,8 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { TagGroupService } from "./tag-group.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; describe("TagGroupService", function () { beforeEach(function () { diff --git a/src/app/services/baw-api/tags.service.spec.ts b/src/app/services/baw-api/tests/tags.service.spec.ts similarity index 86% rename from src/app/services/baw-api/tags.service.spec.ts rename to src/app/services/baw-api/tests/tags.service.spec.ts index 9f31d05f5..70abeac18 100644 --- a/src/app/services/baw-api/tags.service.spec.ts +++ b/src/app/services/baw-api/tests/tags.service.spec.ts @@ -2,7 +2,6 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { async, TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { Tag } from "@models/Tag"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; import { validateApiCreate, validateApiDestroy, @@ -10,10 +9,11 @@ import { validateApiList, validateApiShow, validateApiUpdate, -} from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { TagsService } from "./tags.service"; +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { TagsService } from "../tags.service"; describe("TagsService", function () { beforeEach(async(function () { diff --git a/src/app/services/baw-api/user.service.spec.ts b/src/app/services/baw-api/tests/user.service.spec.ts similarity index 77% rename from src/app/services/baw-api/user.service.spec.ts rename to src/app/services/baw-api/tests/user.service.spec.ts index d35a4c070..3a26bb6e7 100644 --- a/src/app/services/baw-api/user.service.spec.ts +++ b/src/app/services/baw-api/tests/user.service.spec.ts @@ -2,11 +2,11 @@ import { HttpClientTestingModule } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { User } from "@models/User"; +import { validateApiShow } from "src/app/test/helpers/api-common"; import { testAppInitializer } from "src/app/test/helpers/testbed"; -import { validateApiShow } from "../../test/helpers/api-common"; -import { BawApiService } from "./baw-api.service"; -import { MockBawApiService } from "./mock/baseApiMock.service"; -import { UserService } from "./user.service"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { UserService } from "../user.service"; describe("UserService", function () { beforeEach(function () { From 9919fb7cd4a0c367f5479b031c675066d927ef70 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Mon, 27 Apr 2020 15:28:31 +1000 Subject: [PATCH 10/29] Created progress event services --- src/app/services/baw-api/ServiceProviders.ts | 8 +++ src/app/services/baw-api/ServiceTokens.ts | 4 ++ .../baw-api/progress-events.service.ts | 55 ++++++++++++++++++ .../tests/progress-events.service.spec.ts | 58 +++++++++++++++++++ src/app/test/helpers/testbed.ts | 2 + 5 files changed, 127 insertions(+) create mode 100644 src/app/services/baw-api/progress-events.service.ts create mode 100644 src/app/services/baw-api/tests/progress-events.service.spec.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index 06ce8d1d7..a730f9538 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -13,6 +13,10 @@ import { AudioEventsService, } from "./audio-events.service"; import { bookmarkResolvers, BookmarksService } from "./bookmarks.service"; +import { + progressEventResolvers, + ProgressEventsService, +} from "./progress-events.service"; import { projectResolvers, ProjectsService } from "./projects.service"; import { questionResolvers, @@ -38,6 +42,7 @@ import { AUDIO_EVENT, AUDIO_EVENT_COMMENT, BOOKMARK, + PROGRESS_EVENT, PROJECT, QUESTION, RESPONSE, @@ -71,6 +76,7 @@ const services = [ AudioEventCommentsService, AudioEventsService, BookmarksService, + ProgressEventsService, ProjectsService, QuestionsService, ShallowQuestionsService, @@ -93,6 +99,7 @@ const services = [ }, { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, { provide: BOOKMARK.token, useExisting: BookmarksService }, + { provide: PROGRESS_EVENT.token, useExisting: ProgressEventsService }, { provide: PROJECT.token, useExisting: ProjectsService }, { provide: QUESTION.token, useExisting: QuestionsService }, { provide: SHALLOW_QUESTION.token, useExisting: ShallowQuestionsService }, @@ -115,6 +122,7 @@ const resolvers = [ ...audioEventCommentResolvers.providers, ...audioEventResolvers.providers, ...bookmarkResolvers.providers, + ...progressEventResolvers.providers, ...projectResolvers.providers, ...questionResolvers.providers, ...shallowQuestionResolvers.providers, diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 3d945fe38..901739ab1 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -4,6 +4,7 @@ import type { AnalysisJobsService } from "./analysis-jobs.service"; import type { AudioEventCommentsService } from "./audio-event-comments.service"; import type { AudioEventsService } from "./audio-events.service"; import type { BookmarksService } from "./bookmarks.service"; +import type { ProgressEventsService } from "./progress-events.service"; import type { ProjectsService } from "./projects.service"; import type { QuestionsService, @@ -44,6 +45,9 @@ export const AUDIO_EVENT_COMMENT = new ServiceToken( "AUDIO_EVENT_COMMENTS_SERVICE" ); export const BOOKMARK = new ServiceToken("BOOKMARKS_SERVICE"); +export const PROGRESS_EVENT = new ServiceToken( + "PROGRESS_EVENTS_SERVICE" +); export const PROJECT = new ServiceToken("PROJECTS_SERVICE"); export const QUESTION = new ServiceToken("QUESTIONS_SERVICE"); export const SHALLOW_QUESTION = new ServiceToken( diff --git a/src/app/services/baw-api/progress-events.service.ts b/src/app/services/baw-api/progress-events.service.ts new file mode 100644 index 000000000..8b271b183 --- /dev/null +++ b/src/app/services/baw-api/progress-events.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable, Injector } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { ProgressEvent } from "@models/ProgressEvent"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const progressEventId: IdParamOptional = id; +const endpoint = stringTemplate`/studies/${progressEventId}${option}`; + +@Injectable() +export class ProgressEventsService extends StandardApi { + constructor( + http: HttpClient, + @Inject(API_ROOT) apiRoot: string, + injector: Injector + ) { + super(http, apiRoot, ProgressEvent, injector); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: ProgressEvent): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: ProgressEvent): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const progressEventResolvers = new Resolvers< + ProgressEvent, + ProgressEventsService +>([ProgressEventsService], "progressEventId").create("ProgressEvent"); diff --git a/src/app/services/baw-api/tests/progress-events.service.spec.ts b/src/app/services/baw-api/tests/progress-events.service.spec.ts new file mode 100644 index 000000000..dcbfdb2d2 --- /dev/null +++ b/src/app/services/baw-api/tests/progress-events.service.spec.ts @@ -0,0 +1,58 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { ProgressEvent } from "@models/ProgressEvent"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; +import { ProgressEventsService } from "../progress-events.service"; + +describe("ProgressEventsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + ProgressEventsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(ProgressEventsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/progress_events/"); + validateApiFilter( + "/progress_events/filter" + ); + validateApiShow( + "/progress_events/5", + 5, + new ProgressEvent({ id: 5 }) + ); + validateApiCreate( + "/progress_events/", + new ProgressEvent({ id: 5 }) + ); + validateApiUpdate( + "/progress_events/5", + new ProgressEvent({ id: 5 }) + ); + validateApiDestroy( + "/progress_events/5", + 5, + new ProgressEvent({ id: 5 }) + ); +}); diff --git a/src/app/test/helpers/testbed.ts b/src/app/test/helpers/testbed.ts index b8bd72d6a..538ca80af 100644 --- a/src/app/test/helpers/testbed.ts +++ b/src/app/test/helpers/testbed.ts @@ -15,6 +15,7 @@ import { MockImmutableApiService } from "@baw-api/mock/immutableApiMock.service" import { MockSecurityService } from "@baw-api/mock/securityMock.service"; import { MockShowApiService } from "@baw-api/mock/showApiMock.service"; import { MockStandardApiService } from "@baw-api/mock/standardApiMock.service"; +import { ProgressEventsService } from "@baw-api/progress-events.service"; import { ProjectsService } from "@baw-api/projects.service"; import { QuestionsService, @@ -83,6 +84,7 @@ export const testBawServices = [ { provide: AudioEventCommentsService, useClass: MockStandardApiService }, { provide: AudioEventsService, useClass: MockStandardApiService }, { provide: BookmarksService, useClass: MockStandardApiService }, + { provide: ProgressEventsService, useClass: MockStandardApiService }, { provide: ProjectsService, useClass: MockStandardApiService }, { provide: QuestionsService, useClass: MockStandardApiService }, { provide: ShallowQuestionsService, useClass: MockStandardApiService }, From 1c8c53762a83dbbf6edf7537abd8abaf2beb11f3 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 10:07:47 +1000 Subject: [PATCH 11/29] Created dataset services - Removed audio-event-comment service - Cleanup ServiceTokens - Cleanup testBawServices --- src/app/services/baw-api/ServiceProviders.ts | 235 +++++++++++------- src/app/services/baw-api/ServiceTokens.ts | 12 +- .../baw-api/audio-event-comments.service.ts | 74 ------ .../services/baw-api/dataset-items.service.ts | 63 +++++ src/app/services/baw-api/datasets.service.ts | 55 ++++ .../baw-api/progress-events.service.ts | 2 +- .../audio-event-comments.service.spec.ts | 69 ----- .../tests/dataset-items.service.spec.ts | 69 +++++ .../baw-api/tests/datasets.service.spec.ts | 56 +++++ src/app/test/helpers/testbed.ts | 43 +--- 10 files changed, 397 insertions(+), 281 deletions(-) delete mode 100644 src/app/services/baw-api/audio-event-comments.service.ts create mode 100644 src/app/services/baw-api/dataset-items.service.ts create mode 100644 src/app/services/baw-api/datasets.service.ts delete mode 100644 src/app/services/baw-api/tests/audio-event-comments.service.spec.ts create mode 100644 src/app/services/baw-api/tests/dataset-items.service.spec.ts create mode 100644 src/app/services/baw-api/tests/datasets.service.spec.ts diff --git a/src/app/services/baw-api/ServiceProviders.ts b/src/app/services/baw-api/ServiceProviders.ts index a730f9538..65553c198 100644 --- a/src/app/services/baw-api/ServiceProviders.ts +++ b/src/app/services/baw-api/ServiceProviders.ts @@ -4,15 +4,19 @@ import { analysisJobResolvers, AnalysisJobsService, } from "./analysis-jobs.service"; -import { - audioEventCommentResolvers, - AudioEventCommentsService, -} from "./audio-event-comments.service"; import { audioEventResolvers, AudioEventsService, } from "./audio-events.service"; import { bookmarkResolvers, BookmarksService } from "./bookmarks.service"; +import { + datasetItemResolvers, + DatasetItemsService, +} from "./dataset-items.service"; +import { datasetResolvers, DatasetsService } from "./datasets.service"; +import { MockImmutableApiService } from "./mock/immutableApiMock.service"; +import { MockShowApiService } from "./mock/showApiMock.service"; +import { MockStandardApiService } from "./mock/standardApiMock.service"; import { progressEventResolvers, ProgressEventsService, @@ -36,28 +40,7 @@ import { } from "./saved-searches.service"; import { scriptResolvers, ScriptsService } from "./scripts.service"; import { SecurityService } from "./security.service"; -import { - ACCOUNT, - ANALYSIS_JOB, - AUDIO_EVENT, - AUDIO_EVENT_COMMENT, - BOOKMARK, - PROGRESS_EVENT, - PROJECT, - QUESTION, - RESPONSE, - SAVED_SEARCH, - SCRIPT, - SECURITY, - SHALLOW_QUESTION, - SHALLOW_RESPONSE, - SHALLOW_SITE, - SITE, - STUDY, - TAG, - TAG_GROUP, - USER, -} from "./ServiceTokens"; +import * as Tokens from "./ServiceTokens"; import { shallowSiteResolvers, ShallowSitesService, @@ -69,73 +52,139 @@ import { tagGroupResolvers, TagGroupService } from "./tag-group.service"; import { tagResolvers, TagsService } from "./tags.service"; import { userResolvers, UserService } from "./user.service"; -const services = [ - AppConfigService, - AccountService, - AnalysisJobsService, - AudioEventCommentsService, - AudioEventsService, - BookmarksService, - ProgressEventsService, - ProjectsService, - QuestionsService, - ShallowQuestionsService, - ResponsesService, - ShallowResponsesService, - SavedSearchesService, - ScriptsService, - SecurityService, - ShallowSitesService, - SitesService, - StudiesService, - TagsService, - TagGroupService, - UserService, - { provide: ACCOUNT.token, useExisting: AccountService }, - { provide: ANALYSIS_JOB.token, useExisting: AnalysisJobsService }, - { - provide: AUDIO_EVENT_COMMENT.token, - useExisting: AudioEventCommentsService, - }, - { provide: AUDIO_EVENT.token, useExisting: AudioEventsService }, - { provide: BOOKMARK.token, useExisting: BookmarksService }, - { provide: PROGRESS_EVENT.token, useExisting: ProgressEventsService }, - { provide: PROJECT.token, useExisting: ProjectsService }, - { provide: QUESTION.token, useExisting: QuestionsService }, - { provide: SHALLOW_QUESTION.token, useExisting: ShallowQuestionsService }, - { provide: RESPONSE.token, useExisting: ResponsesService }, - { provide: SHALLOW_RESPONSE.token, useExisting: ShallowResponsesService }, - { provide: SAVED_SEARCH.token, useExisting: SavedSearchesService }, - { provide: SCRIPT.token, useExisting: ScriptsService }, - { provide: SECURITY.token, useExisting: SecurityService }, - { provide: SHALLOW_SITE.token, useExisting: ShallowSitesService }, - { provide: SITE.token, useExisting: SitesService }, - { provide: STUDY.token, useExisting: StudiesService }, - { provide: TAG.token, useExisting: TagsService }, - { provide: TAG_GROUP.token, useExisting: TagGroupService }, - { provide: USER.token, useExisting: UserService }, +const serviceList = [ + { + token: Tokens.ACCOUNT, + service: AccountService, + resolvers: accountResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.ANALYSIS_JOB, + service: AnalysisJobsService, + resolvers: analysisJobResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.AUDIO_EVENT, + service: AudioEventsService, + resolvers: audioEventResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.BOOKMARK, + service: BookmarksService, + resolvers: bookmarkResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.DATASET, + service: DatasetsService, + resolvers: datasetResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.DATASET_ITEM, + service: DatasetItemsService, + resolvers: datasetItemResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.PROGRESS_EVENT, + service: ProgressEventsService, + resolvers: progressEventResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.PROJECT, + service: ProjectsService, + resolvers: projectResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.QUESTION, + service: QuestionsService, + resolvers: questionResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.SHALLOW_QUESTION, + service: ShallowQuestionsService, + resolvers: shallowQuestionResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.RESPONSE, + service: ResponsesService, + resolvers: responseResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.SHALLOW_RESPONSE, + service: ShallowResponsesService, + resolvers: shallowResponseResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.SAVED_SEARCH, + service: SavedSearchesService, + resolvers: savedSearchResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.SCRIPT, + service: ScriptsService, + resolvers: scriptResolvers, + mock: MockImmutableApiService, + }, + { + token: Tokens.SITE, + service: SitesService, + resolvers: siteResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.SHALLOW_SITE, + service: ShallowSitesService, + resolvers: shallowSiteResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.STUDY, + service: StudiesService, + resolvers: studyResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.TAG, + service: TagsService, + resolvers: tagResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.TAG_GROUP, + service: TagGroupService, + resolvers: tagGroupResolvers, + mock: MockStandardApiService, + }, + { + token: Tokens.USER, + service: UserService, + resolvers: userResolvers, + mock: MockShowApiService, + }, ]; -const resolvers = [ - ...accountResolvers.providers, - ...analysisJobResolvers.providers, - ...audioEventCommentResolvers.providers, - ...audioEventResolvers.providers, - ...bookmarkResolvers.providers, - ...progressEventResolvers.providers, - ...projectResolvers.providers, - ...questionResolvers.providers, - ...shallowQuestionResolvers.providers, - ...responseResolvers.providers, - ...shallowResponseResolvers.providers, - ...savedSearchResolvers.providers, - ...scriptResolvers.providers, - ...siteResolvers.providers, - ...shallowSiteResolvers.providers, - ...studyResolvers.providers, - ...tagResolvers.providers, - ...tagGroupResolvers.providers, - ...userResolvers.providers, -]; +const serviceProviders: any[] = [AppConfigService, SecurityService]; + +serviceList.forEach((service) => { + serviceProviders.push( + ...[ + service.service, + { provide: service.token, useExisting: service.service }, + ...service.resolvers.providers, + ] + ); +}); -export const serviceProviders = [...services, ...resolvers]; +export { serviceProviders, serviceList }; diff --git a/src/app/services/baw-api/ServiceTokens.ts b/src/app/services/baw-api/ServiceTokens.ts index 901739ab1..ea11dd8f7 100644 --- a/src/app/services/baw-api/ServiceTokens.ts +++ b/src/app/services/baw-api/ServiceTokens.ts @@ -1,9 +1,10 @@ import { InjectionToken } from "@angular/core"; import type { AccountService } from "./account.service"; import type { AnalysisJobsService } from "./analysis-jobs.service"; -import type { AudioEventCommentsService } from "./audio-event-comments.service"; import type { AudioEventsService } from "./audio-events.service"; import type { BookmarksService } from "./bookmarks.service"; +import type { DatasetItemsService } from "./dataset-items.service"; +import type { DatasetsService } from "./datasets.service"; import type { ProgressEventsService } from "./progress-events.service"; import type { ProjectsService } from "./projects.service"; import type { @@ -16,7 +17,6 @@ import type { } from "./responses.service"; import type { SavedSearchesService } from "./saved-searches.service"; import type { ScriptsService } from "./scripts.service"; -import type { SecurityService } from "./security.service"; import type { ShallowSitesService, SitesService } from "./sites.service"; import type { StudiesService } from "./studies.service"; import type { TagGroupService } from "./tag-group.service"; @@ -41,10 +41,11 @@ export const ANALYSIS_JOB = new ServiceToken( export const AUDIO_EVENT = new ServiceToken( "AUDIO_EVENTS_SERVICE" ); -export const AUDIO_EVENT_COMMENT = new ServiceToken( - "AUDIO_EVENT_COMMENTS_SERVICE" -); export const BOOKMARK = new ServiceToken("BOOKMARKS_SERVICE"); +export const DATASET = new ServiceToken("DATASETS_SERVICE"); +export const DATASET_ITEM = new ServiceToken( + "DATASET_ITEMS_SERVICE" +); export const PROGRESS_EVENT = new ServiceToken( "PROGRESS_EVENTS_SERVICE" ); @@ -61,7 +62,6 @@ export const SAVED_SEARCH = new ServiceToken( "SAVED_SEARCHES_SERVICE" ); export const SCRIPT = new ServiceToken("SCRIPTS_SERVICE"); -export const SECURITY = new ServiceToken("SECURITY_SERVICE"); export const SHALLOW_SITE = new ServiceToken( "SHALLOW_SITES_SERVICE" ); diff --git a/src/app/services/baw-api/audio-event-comments.service.ts b/src/app/services/baw-api/audio-event-comments.service.ts deleted file mode 100644 index 0c6b887bd..000000000 --- a/src/app/services/baw-api/audio-event-comments.service.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { HttpClient } from "@angular/common/http"; -import { Inject, Injectable } from "@angular/core"; -import { API_ROOT } from "@helpers/app-initializer/app-initializer"; -import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; -import { AnalysisJob } from "@models/AnalysisJob"; -import { AudioEventComment } from "@models/AudioEventComment"; -import { Observable } from "rxjs"; -import { - Empty, - Filter, - id, - IdOr, - IdParam, - IdParamOptional, - option, - StandardApi, -} from "./api-common"; -import { Filters } from "./baw-api.service"; -import { Resolvers } from "./resolver-common"; - -const analysisJobId: IdParam = id; -const audioEventCommentId: IdParamOptional = id; -const endpoint = stringTemplate`/analysis_jobs/${analysisJobId}/comments/${audioEventCommentId}${option}`; - -@Injectable() -export class AudioEventCommentsService extends StandardApi< - AudioEventComment, - [IdOr] -> { - constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { - super(http, apiRoot, AudioEventComment); - } - - list(analysis: IdOr): Observable { - return this.apiList(endpoint(analysis, Empty, Empty)); - } - filter( - filters: Filters, - analysis: IdOr - ): Observable { - return this.apiFilter(endpoint(analysis, Empty, Filter), filters); - } - show( - model: IdOr, - analysis: IdOr - ): Observable { - return this.apiShow(endpoint(analysis, model, Empty)); - } - create( - model: AudioEventComment, - analysis: IdOr - ): Observable { - return this.apiCreate(endpoint(analysis, Empty, Empty), model); - } - update( - model: AudioEventComment, - analysis: IdOr - ): Observable { - return this.apiUpdate(endpoint(analysis, model, Empty), model); - } - destroy( - model: IdOr, - analysis: IdOr - ): Observable { - return this.apiDestroy(endpoint(analysis, model, Empty)); - } -} - -export const audioEventCommentResolvers = new Resolvers< - AudioEventComment, - AudioEventCommentsService ->([AudioEventCommentsService], "audioEventCommentId", ["analysisJobId"]).create( - "AudioEventComment" -); diff --git a/src/app/services/baw-api/dataset-items.service.ts b/src/app/services/baw-api/dataset-items.service.ts new file mode 100644 index 000000000..1048ff439 --- /dev/null +++ b/src/app/services/baw-api/dataset-items.service.ts @@ -0,0 +1,63 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Dataset } from "@models/Dataset"; +import { DatasetItem } from "@models/DatasetItem"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParam, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const datasetId: IdParam = id; +const datasetItemId: IdParamOptional = id; +const endpoint = stringTemplate`/datasets/${datasetId}/items/${datasetItemId}${option}`; + +@Injectable() +export class DatasetItemsService extends StandardApi< + DatasetItem, + [IdOr] +> { + constructor(http: HttpClient, @Inject(API_ROOT) apiRoot: string) { + super(http, apiRoot, DatasetItem); + } + + list(dataset: IdOr): Observable { + return this.apiList(endpoint(dataset, Empty, Empty)); + } + filter(filters: Filters, dataset: IdOr): Observable { + return this.apiFilter(endpoint(dataset, Empty, Filter), filters); + } + show( + model: IdOr, + dataset: IdOr + ): Observable { + return this.apiShow(endpoint(dataset, model, Empty)); + } + create(model: DatasetItem, dataset: IdOr): Observable { + return this.apiCreate(endpoint(dataset, Empty, Empty), model); + } + update(model: DatasetItem, dataset: IdOr): Observable { + return this.apiUpdate(endpoint(dataset, model, Empty), model); + } + destroy( + model: IdOr, + dataset: IdOr + ): Observable { + return this.apiDestroy(endpoint(dataset, model, Empty)); + } +} + +export const datasetItemResolvers = new Resolvers< + DatasetItem, + DatasetItemsService +>([DatasetItemsService], "datasetItemId").create("DatasetItem"); diff --git a/src/app/services/baw-api/datasets.service.ts b/src/app/services/baw-api/datasets.service.ts new file mode 100644 index 000000000..6eaee91a2 --- /dev/null +++ b/src/app/services/baw-api/datasets.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from "@angular/common/http"; +import { Inject, Injectable, Injector } from "@angular/core"; +import { API_ROOT } from "@helpers/app-initializer/app-initializer"; +import { stringTemplate } from "@helpers/stringTemplate/stringTemplate"; +import { Dataset } from "@models/Dataset"; +import { Observable } from "rxjs"; +import { + Empty, + Filter, + id, + IdOr, + IdParamOptional, + option, + StandardApi, +} from "./api-common"; +import { Filters } from "./baw-api.service"; +import { Resolvers } from "./resolver-common"; + +const datasetId: IdParamOptional = id; +const endpoint = stringTemplate`/datasets/${datasetId}${option}`; + +@Injectable() +export class DatasetsService extends StandardApi { + constructor( + http: HttpClient, + @Inject(API_ROOT) apiRoot: string, + injector: Injector + ) { + super(http, apiRoot, Dataset, injector); + } + + list(): Observable { + return this.apiList(endpoint(Empty, Empty)); + } + filter(filters: Filters): Observable { + return this.apiFilter(endpoint(Empty, Filter), filters); + } + show(model: IdOr): Observable { + return this.apiShow(endpoint(model, Empty)); + } + create(model: Dataset): Observable { + return this.apiCreate(endpoint(Empty, Empty), model); + } + update(model: Dataset): Observable { + return this.apiUpdate(endpoint(model, Empty), model); + } + destroy(model: IdOr): Observable { + return this.apiDestroy(endpoint(model, Empty)); + } +} + +export const datasetResolvers = new Resolvers( + [DatasetsService], + "datasetId" +).create("Dataset"); diff --git a/src/app/services/baw-api/progress-events.service.ts b/src/app/services/baw-api/progress-events.service.ts index 8b271b183..b87cb4d58 100644 --- a/src/app/services/baw-api/progress-events.service.ts +++ b/src/app/services/baw-api/progress-events.service.ts @@ -17,7 +17,7 @@ import { Filters } from "./baw-api.service"; import { Resolvers } from "./resolver-common"; const progressEventId: IdParamOptional = id; -const endpoint = stringTemplate`/studies/${progressEventId}${option}`; +const endpoint = stringTemplate`/progress_events/${progressEventId}${option}`; @Injectable() export class ProgressEventsService extends StandardApi { diff --git a/src/app/services/baw-api/tests/audio-event-comments.service.spec.ts b/src/app/services/baw-api/tests/audio-event-comments.service.spec.ts deleted file mode 100644 index cee628f67..000000000 --- a/src/app/services/baw-api/tests/audio-event-comments.service.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { TestBed } from "@angular/core/testing"; -import { RouterTestingModule } from "@angular/router/testing"; -import { AudioEventComment } from "@models/AudioEventComment"; -import { - validateApiCreate, - validateApiDestroy, - validateApiFilter, - validateApiList, - validateApiShow, - validateApiUpdate, -} from "src/app/test/helpers/api-common"; -import { testAppInitializer } from "src/app/test/helpers/testbed"; -import { AudioEventCommentsService } from "../audio-event-comments.service"; -import { BawApiService } from "../baw-api.service"; -import { MockBawApiService } from "../mock/baseApiMock.service"; - -describe("AudioEventCommentsService", function () { - beforeEach(function () { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - ...testAppInitializer, - AudioEventCommentsService, - { provide: BawApiService, useClass: MockBawApiService }, - ], - }); - - this.service = TestBed.inject(AudioEventCommentsService); - }); - - it("should be created", function () { - expect(this.service).toBeTruthy(); - }); - - validateApiList( - "/analysis_jobs/5/comments/", - undefined, - 5 - ); - validateApiFilter( - "/analysis_jobs/5/comments/filter", - undefined, - undefined, - 5 - ); - validateApiShow( - "/analysis_jobs/5/comments/10", - 10, - new AudioEventComment({ id: 10 }), - 5 - ); - validateApiCreate( - "/analysis_jobs/5/comments/", - new AudioEventComment({ id: 10 }), - 5 - ); - validateApiUpdate( - "/analysis_jobs/5/comments/10", - new AudioEventComment({ id: 10 }), - 5 - ); - validateApiDestroy( - "/analysis_jobs/5/comments/10", - 10, - new AudioEventComment({ id: 10 }), - 5 - ); -}); diff --git a/src/app/services/baw-api/tests/dataset-items.service.spec.ts b/src/app/services/baw-api/tests/dataset-items.service.spec.ts new file mode 100644 index 000000000..57e5a4032 --- /dev/null +++ b/src/app/services/baw-api/tests/dataset-items.service.spec.ts @@ -0,0 +1,69 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { DatasetItem } from "@models/DatasetItem"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { DatasetItemsService } from "../dataset-items.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; + +describe("DatasetItemsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + DatasetItemsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(DatasetItemsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList( + "/datasets/5/items/", + undefined, + 5 + ); + validateApiFilter( + "/datasets/5/items/filter", + undefined, + undefined, + 5 + ); + validateApiShow( + "/datasets/5/items/10", + 10, + new DatasetItem({ id: 10 }), + 5 + ); + validateApiCreate( + "/datasets/5/items/", + new DatasetItem({ id: 10 }), + 5 + ); + validateApiUpdate( + "/datasets/5/items/10", + new DatasetItem({ id: 10 }), + 5 + ); + validateApiDestroy( + "/datasets/5/items/10", + 10, + new DatasetItem({ id: 10 }), + 5 + ); +}); diff --git a/src/app/services/baw-api/tests/datasets.service.spec.ts b/src/app/services/baw-api/tests/datasets.service.spec.ts new file mode 100644 index 000000000..b5d22d5d6 --- /dev/null +++ b/src/app/services/baw-api/tests/datasets.service.spec.ts @@ -0,0 +1,56 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Dataset } from "@models/Dataset"; +import { + validateApiCreate, + validateApiDestroy, + validateApiFilter, + validateApiList, + validateApiShow, + validateApiUpdate, +} from "src/app/test/helpers/api-common"; +import { testAppInitializer } from "src/app/test/helpers/testbed"; +import { BawApiService } from "../baw-api.service"; +import { DatasetsService } from "../datasets.service"; +import { MockBawApiService } from "../mock/baseApiMock.service"; + +describe("DatasetsService", function () { + beforeEach(function () { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + ...testAppInitializer, + DatasetsService, + { provide: BawApiService, useClass: MockBawApiService }, + ], + }); + + this.service = TestBed.inject(DatasetsService); + }); + + it("should be created", function () { + expect(this.service).toBeTruthy(); + }); + + validateApiList("/datasets/"); + validateApiFilter("/datasets/filter"); + validateApiShow( + "/datasets/5", + 5, + new Dataset({ id: 5 }) + ); + validateApiCreate( + "/datasets/", + new Dataset({ id: 5 }) + ); + validateApiUpdate( + "/datasets/5", + new Dataset({ id: 5 }) + ); + validateApiDestroy( + "/datasets/5", + 5, + new Dataset({ id: 5 }) + ); +}); diff --git a/src/app/test/helpers/testbed.ts b/src/app/test/helpers/testbed.ts index 538ca80af..8dab1d871 100644 --- a/src/app/test/helpers/testbed.ts +++ b/src/app/test/helpers/testbed.ts @@ -1,35 +1,15 @@ import { HTTP_INTERCEPTORS } from "@angular/common/http"; import { Params } from "@angular/router"; -import { AccountService } from "@baw-api/account.service"; -import { AnalysisJobsService } from "@baw-api/analysis-jobs.service"; import { BawApiInterceptor } from "@baw-api/api.interceptor.service"; -import { AudioEventCommentsService } from "@baw-api/audio-event-comments.service"; -import { AudioEventsService } from "@baw-api/audio-events.service"; import { BawApiService, STUB_MODEL_BUILDER } from "@baw-api/baw-api.service"; -import { BookmarksService } from "@baw-api/bookmarks.service"; import { MockBawApiService, MockModel, } from "@baw-api/mock/baseApiMock.service"; -import { MockImmutableApiService } from "@baw-api/mock/immutableApiMock.service"; import { MockSecurityService } from "@baw-api/mock/securityMock.service"; -import { MockShowApiService } from "@baw-api/mock/showApiMock.service"; -import { MockStandardApiService } from "@baw-api/mock/standardApiMock.service"; -import { ProgressEventsService } from "@baw-api/progress-events.service"; -import { ProjectsService } from "@baw-api/projects.service"; -import { - QuestionsService, - ShallowQuestionsService, -} from "@baw-api/questions.service"; import { ResolvedModel } from "@baw-api/resolver-common"; -import { SavedSearchesService } from "@baw-api/saved-searches.service"; -import { ScriptsService } from "@baw-api/scripts.service"; import { SecurityService } from "@baw-api/security.service"; -import { ShallowSitesService, SitesService } from "@baw-api/sites.service"; -import { StudiesService } from "@baw-api/studies.service"; -import { TagGroupService } from "@baw-api/tag-group.service"; -import { TagsService } from "@baw-api/tags.service"; -import { UserService } from "@baw-api/user.service"; +import { serviceList } from "@baw-api/ServiceProviders"; import { API_CONFIG, API_ROOT, @@ -79,23 +59,10 @@ export const testBawServices = [ { provide: STUB_MODEL_BUILDER, useValue: MockModel }, { provide: BawApiService, useClass: MockBawApiService }, { provide: SecurityService, useClass: MockSecurityService }, - { provide: AccountService, useClass: MockStandardApiService }, - { provide: AnalysisJobsService, useClass: MockStandardApiService }, - { provide: AudioEventCommentsService, useClass: MockStandardApiService }, - { provide: AudioEventsService, useClass: MockStandardApiService }, - { provide: BookmarksService, useClass: MockStandardApiService }, - { provide: ProgressEventsService, useClass: MockStandardApiService }, - { provide: ProjectsService, useClass: MockStandardApiService }, - { provide: QuestionsService, useClass: MockStandardApiService }, - { provide: ShallowQuestionsService, useClass: MockStandardApiService }, - { provide: SavedSearchesService, useClass: MockStandardApiService }, - { provide: ScriptsService, useClass: MockImmutableApiService }, - { provide: SitesService, useClass: MockStandardApiService }, - { provide: ShallowSitesService, useClass: MockStandardApiService }, - { provide: StudiesService, useClass: MockStandardApiService }, - { provide: TagsService, useClass: MockStandardApiService }, - { provide: TagGroupService, useClass: MockStandardApiService }, - { provide: UserService, useClass: MockShowApiService }, + ...serviceList.map((service) => ({ + provide: service.service, + useClass: service.mock, + })), ]; /** From 93f3e69e7da50be6c70787a50efc6e7da31638a6 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 11:53:02 +1000 Subject: [PATCH 12/29] Cleanup AnalysisJob review comments Fix the following review comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415440228 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415440310 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415448544 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415448544 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415448646 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415449163 --- src/app/models/AnalysisJob.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/app/models/AnalysisJob.ts b/src/app/models/AnalysisJob.ts index 3033692f9..a0d1a85a3 100644 --- a/src/app/models/AnalysisJob.ts +++ b/src/app/models/AnalysisJob.ts @@ -1,4 +1,4 @@ -import { ACCOUNT, SCRIPT } from "@baw-api/ServiceTokens"; +import { ACCOUNT, SAVED_SEARCH, SCRIPT } from "@baw-api/ServiceTokens"; import { Duration } from "luxon"; import { Observable } from "rxjs"; import { @@ -14,6 +14,7 @@ import { BawPersistAttr, HasOne, } from "./AbstractModel"; +import type { SavedSearch } from "./SavedSearch"; import type { Script } from "./Script"; import type { User } from "./User"; @@ -24,7 +25,7 @@ export interface IAnalysisJob { id?: Id; name?: Param; annotationName?: string; - customSettings?: Blob | any; + customSettings?: Blob; scriptId?: Id; creatorId?: Id; updaterId?: Id; @@ -37,7 +38,7 @@ export interface IAnalysisJob { startedAt?: DateTimeTimezone | string; overallStatus?: Status; overallStatusModifiedAt?: DateTimeTimezone | string; - overallProgress?: Blob | any; + overallProgress?: object; overallProgressModifiedAt?: DateTimeTimezone | string; overallCount?: number; overallDurationSeconds?: number; @@ -72,7 +73,7 @@ export class AnalysisJob extends AbstractModel implements IAnalysisJob { public readonly overallStatus?: Status; @BawDateTime() public readonly overallStatusModifiedAt?: DateTimeTimezone; - public readonly overallProgress?: Blob; + public readonly overallProgress?: object; @BawDateTime() public readonly overallProgressModifiedAt?: DateTimeTimezone; public readonly overallCount?: number; @@ -90,19 +91,22 @@ export class AnalysisJob extends AbstractModel implements IAnalysisJob { public updater?: Observable; @HasOne(ACCOUNT, (m: AnalysisJob) => m.deleterId) public deleter?: Observable; - // TODO Add SavedSearch Association + @HasOne(SAVED_SEARCH, (m: AnalysisJob) => m.savedSearchId) + public savedSearch?: Observable; constructor(analysisJob: IAnalysisJob) { super(analysisJob); - - this.customSettings = new Blob([analysisJob.customSettings]); - this.overallProgress = new Blob([analysisJob.overallProgress]); } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("AnalysisJob viewUrl not implemented."); } } -// TODO -type Status = "??? Anthony"; +type Status = + | "before_save" + | "new" + | "preparing" + | "processing" + | "suspended" + | "completed"; From 23dce4a07aa36a65a276827fbfe42d5d0d0030c4 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 12:04:14 +1000 Subject: [PATCH 13/29] Cleanup AnalysisJobItem review comments Fix the following review comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415449741 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415449858 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415450122 --- src/app/models/AnalysisJobItem.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/app/models/AnalysisJobItem.ts b/src/app/models/AnalysisJobItem.ts index b8d58f2fe..2063c38b2 100644 --- a/src/app/models/AnalysisJobItem.ts +++ b/src/app/models/AnalysisJobItem.ts @@ -1,5 +1,8 @@ +import { ANALYSIS_JOB } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; -import { AbstractModel, BawDateTime, BawPersistAttr } from "./AbstractModel"; +import { Observable } from "rxjs"; +import { AbstractModel, BawDateTime, HasOne } from "./AbstractModel"; +import type { AnalysisJob } from "./AnalysisJob"; export interface IAnalysisJobItem { id?: Id; @@ -16,13 +19,9 @@ export interface IAnalysisJobItem { export class AnalysisJobItem extends AbstractModel implements IAnalysisJobItem { public readonly kind: "AnalysisJobItem" = "AnalysisJobItem"; - @BawPersistAttr public readonly id?: Id; - @BawPersistAttr public readonly analysisJobId?: Id; - @BawPersistAttr public readonly audioRecordingId?: Id; - @BawPersistAttr public readonly queueId?: string; public readonly status?: Status; @BawDateTime() @@ -37,16 +36,25 @@ export class AnalysisJobItem extends AbstractModel implements IAnalysisJobItem { public readonly cancelStartedAt?: DateTimeTimezone; // Associations - // TODO Add AnalysisJob, AudioRecording, Queue associations + @HasOne(ANALYSIS_JOB, (m: AnalysisJobItem) => m.analysisJobId) + public analysisJob?: Observable; + // TODO Add AudioRecording, Queue associations constructor(analysisJobItem: IAnalysisJobItem) { super(analysisJobItem); } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("AnalysisJobItem viewUrl not implemented."); } } -// TODO -export type Status = "new" | "??? Anthony"; +export type Status = + | "successful" + | "new" + | "queued" + | "working" + | "failed" + | "timed_out" + | "cancelling" + | "cancelled"; From 0845eea6547b1c7bcf469954723bb2986dc77b11 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 12:12:06 +1000 Subject: [PATCH 14/29] Cleanup AudioEvent models review comments Fix AudioEvent models review comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415452319 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415452407 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415454326 --- src/app/models/AudioEvent.ts | 2 +- src/app/models/AudioEventComment.ts | 74 ----------------------------- src/app/models/AudioEventTag.ts | 8 +++- 3 files changed, 7 insertions(+), 77 deletions(-) delete mode 100644 src/app/models/AudioEventComment.ts diff --git a/src/app/models/AudioEvent.ts b/src/app/models/AudioEvent.ts index 03d4b7891..e36fcd507 100644 --- a/src/app/models/AudioEvent.ts +++ b/src/app/models/AudioEvent.ts @@ -65,6 +65,6 @@ export class AudioEvent extends AbstractModel implements IAudioEvent { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("AudioEvent viewUrl not implemented."); } } diff --git a/src/app/models/AudioEventComment.ts b/src/app/models/AudioEventComment.ts deleted file mode 100644 index 5aea50bd4..000000000 --- a/src/app/models/AudioEventComment.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { ACCOUNT } from "@baw-api/ServiceTokens"; -import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; -import { Observable } from "rxjs"; -import { - AbstractModel, - BawDateTime, - BawPersistAttr, - HasOne, -} from "./AbstractModel"; -import type { User } from "./User"; - -export interface IAudioEventComment { - id?: Id; - audioEventId?: Id; - comment?: Blob; - flag?: Flag; - flagExplain?: Blob; - flaggerId?: Id; - creatorId?: Id; - updaterId?: Id; - deleterId?: Id; - flaggedAt?: DateTimeTimezone | string; - createdAt?: DateTimeTimezone | string; - updatedAt?: DateTimeTimezone | string; - deletedAt?: DateTimeTimezone | string; -} - -export class AudioEventComment extends AbstractModel - implements IAudioEventComment { - public readonly kind: "AudioEventComment" = "AudioEventComment"; - @BawPersistAttr - public readonly id?: Id; - @BawPersistAttr - public readonly audioEventId?: Id; - @BawPersistAttr - public readonly comment?: Blob; - @BawPersistAttr - public readonly flag?: Flag; - @BawPersistAttr - public readonly flagExplain?: Blob; - public readonly flaggerId?: Id; - public readonly creatorId?: Id; - public readonly updaterId?: Id; - public readonly deleterId?: Id; - @BawDateTime() - public readonly flaggedAt?: DateTimeTimezone; - @BawDateTime() - public readonly createdAt?: DateTimeTimezone; - @BawDateTime() - public readonly updatedAt?: DateTimeTimezone; - @BawDateTime() - public readonly deletedAt?: DateTimeTimezone; - - // Associations - @HasOne(ACCOUNT, (m: AudioEventComment) => m.flaggerId) - public flagger?: Observable; - @HasOne(ACCOUNT, (m: AudioEventComment) => m.creatorId) - public creator?: Observable; - @HasOne(ACCOUNT, (m: AudioEventComment) => m.updaterId) - public updater?: Observable; - @HasOne(ACCOUNT, (m: AudioEventComment) => m.deleterId) - public deleter?: Observable; - - constructor(audioEventComment: IAudioEventComment) { - super(audioEventComment); - } - - public get viewUrl(): string { - return "/BROKEN_LINK"; - } -} - -// TODO -type Flag = "??? Anthony"; diff --git a/src/app/models/AudioEventTag.ts b/src/app/models/AudioEventTag.ts index cbe841060..5d91be13f 100644 --- a/src/app/models/AudioEventTag.ts +++ b/src/app/models/AudioEventTag.ts @@ -1,4 +1,4 @@ -import { ACCOUNT, TAG } from "@baw-api/ServiceTokens"; +import { ACCOUNT, TAG, AUDIO_EVENT } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; import { Observable } from "rxjs"; import { @@ -9,6 +9,7 @@ import { } from "./AbstractModel"; import type { Tag } from "./Tag"; import type { User } from "./User"; +import type { AudioEvent } from "./AudioEvent"; export interface IAudioEventTag { id?: Id; @@ -43,12 +44,15 @@ export class AudioEventTag extends AbstractModel implements IAudioEventTag { public creator?: Observable; @HasOne(ACCOUNT, (m: AudioEventTag) => m.updaterId) public updater?: Observable; + @HasOne(AUDIO_EVENT, (m: AudioEventTag) => m.audioEventId) + public audioEvent?: Observable; constructor(audioEventTag: IAudioEventTag) { super(audioEventTag); } public get viewUrl(): string { - return "/BROKEN_LINK"; + // AudioEventTag currently has no plans implementing its own view + throw new Error("AudioEventTag viewUrl not implemented."); } } From 78dfd4f0a4a74084fab57355cba8d79084e093a4 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 12:21:02 +1000 Subject: [PATCH 15/29] Cleanup AudioRecording review comments Fix AudioRecording review comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415454578 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415454734 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415454868 --- src/app/models/AudioRecording.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/app/models/AudioRecording.ts b/src/app/models/AudioRecording.ts index 0687a5f75..d99766139 100644 --- a/src/app/models/AudioRecording.ts +++ b/src/app/models/AudioRecording.ts @@ -6,7 +6,6 @@ import { AbstractModel, BawDateTime, BawDuration, - BawPersistAttr, HasOne, } from "./AbstractModel"; import type { Site } from "./Site"; @@ -45,28 +44,21 @@ export interface IAudioRecording { */ export class AudioRecording extends AbstractModel implements IAudioRecording { public readonly kind: "AudioRecording" = "AudioRecording"; - @BawPersistAttr public readonly id?: Id; - @BawPersistAttr public readonly uuid?: Uuid; public readonly uploaderId?: Id; public readonly recordedDate?: DateTimeTimezone; - @BawPersistAttr public readonly siteId?: Id; @BawDuration({ key: "durationSeconds" }) public readonly duration: Duration; - @BawPersistAttr public readonly durationSeconds?: number; - @BawPersistAttr public readonly sampleRateHertz?: number; - @BawPersistAttr public readonly channels?: number; public readonly bitRateBps?: number; public readonly mediaType?: string; public readonly dataLengthBytes?: number; public readonly fileHash?: string; public readonly status?: Status; - @BawPersistAttr public readonly notes?: Blob; public readonly creatorId?: Id; public readonly updaterId?: Id; @@ -97,8 +89,14 @@ export class AudioRecording extends AbstractModel implements IAudioRecording { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("AudioRecording viewUrl not implemented."); } } -type Status = "ready" | "uploading" | "corrupt"; +type Status = + | "new" + | "uploading" + | "to_check" + | "ready" + | "corrupt" + | "aborted"; From 41241bed717902ddf847dd7c98c632198fcefccb Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 12:43:47 +1000 Subject: [PATCH 16/29] Cleanup viewUrl review comments Cleanup viewUrl review comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455277 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455505 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455541 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455660 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455798 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415455894 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415462023 --- src/app/models/Bookmark.ts | 13 +++++++------ src/app/models/Dataset.ts | 2 +- src/app/models/DatasetItem.ts | 9 ++++++--- src/app/models/ProgressEvent.ts | 9 ++++++--- src/app/models/SavedSearch.ts | 7 ++++--- src/app/models/Study.ts | 2 +- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/app/models/Bookmark.ts b/src/app/models/Bookmark.ts index 598cd1207..357924131 100644 --- a/src/app/models/Bookmark.ts +++ b/src/app/models/Bookmark.ts @@ -27,7 +27,7 @@ export interface IBookmark { createdAt?: DateTimeTimezone | string; updatedAt?: DateTimeTimezone | string; description?: Description; - category?: Category; + category?: string; } export class Bookmark extends AbstractModel implements IBookmark { @@ -49,7 +49,7 @@ export class Bookmark extends AbstractModel implements IBookmark { @BawPersistAttr public readonly description?: Description; @BawPersistAttr - public readonly category?: Category; + public readonly category?: string; // Associations // TODO Create AudioRecording association @@ -62,11 +62,12 @@ export class Bookmark extends AbstractModel implements IBookmark { super(bookmark); } + public listenViewUrl(recordingId: Id, startOffset?: number): string { + throw new Error("Bookmark listenViewUrl not implemented."); + } + public get viewUrl(): string { - return "/BROKEN_LINK"; // return `https://www.ecosounds.org/listen/${this.audioRecordingId}?start=${this.offsetSeconds}&end=${???}`; + throw new Error("Bookmark viewUrl not implemented."); } } - -// TODO -type Category = "<>" | "??? Anthony"; diff --git a/src/app/models/Dataset.ts b/src/app/models/Dataset.ts index 28a7e95d7..96a9ffaab 100644 --- a/src/app/models/Dataset.ts +++ b/src/app/models/Dataset.ts @@ -50,6 +50,6 @@ export class Dataset extends AbstractModel implements IDataset { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("Dataset viewUrl not implemented."); } } diff --git a/src/app/models/DatasetItem.ts b/src/app/models/DatasetItem.ts index 87a0741ef..b6da9d1a2 100644 --- a/src/app/models/DatasetItem.ts +++ b/src/app/models/DatasetItem.ts @@ -1,4 +1,4 @@ -import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { ACCOUNT, DATASET } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; import { Observable } from "rxjs"; import { @@ -7,6 +7,7 @@ import { BawPersistAttr, HasOne, } from "./AbstractModel"; +import type { Dataset } from "./Dataset"; import type { User } from "./User"; export interface IDatasetItem { @@ -39,7 +40,9 @@ export class DatasetItem extends AbstractModel implements IDatasetItem { public readonly order?: number; // Associations - // TODO Create Dataset, AudioRecording association + // TODO Create AudioRecording association + @HasOne(DATASET, (m: DatasetItem) => m.datasetId) + public dataset?: Observable; @HasOne(ACCOUNT, (m: DatasetItem) => m.creatorId) public creator?: Observable; @@ -48,6 +51,6 @@ export class DatasetItem extends AbstractModel implements IDatasetItem { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("DatasetItem viewUrl not implemented."); } } diff --git a/src/app/models/ProgressEvent.ts b/src/app/models/ProgressEvent.ts index d22291e5d..2655995be 100644 --- a/src/app/models/ProgressEvent.ts +++ b/src/app/models/ProgressEvent.ts @@ -1,4 +1,4 @@ -import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { ACCOUNT, DATASET_ITEM } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; import { Observable } from "rxjs"; import { @@ -7,6 +7,7 @@ import { BawPersistAttr, HasOne, } from "./AbstractModel"; +import type { DatasetItem } from "./DatasetItem"; import type { User } from "./User"; export interface IProgressEvent { @@ -30,7 +31,9 @@ export class ProgressEvent extends AbstractModel implements IProgressEvent { public readonly createdAt?: DateTimeTimezone; // Associations - // TODO Add DatasetItem association + // TODO Add DatasetItem association\ + @HasOne(DATASET_ITEM, (m: ProgressEvent) => m.datasetItemId) + public datasetItem?: Observable; @HasOne(ACCOUNT, (m: ProgressEvent) => m.creatorId) public creator?: Observable; @@ -39,6 +42,6 @@ export class ProgressEvent extends AbstractModel implements IProgressEvent { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("ProgressEvent viewUrl not implemented."); } } diff --git a/src/app/models/SavedSearch.ts b/src/app/models/SavedSearch.ts index 3fab67f3e..d35eb602e 100644 --- a/src/app/models/SavedSearch.ts +++ b/src/app/models/SavedSearch.ts @@ -1,3 +1,4 @@ +import { Filters } from "@baw-api/baw-api.service"; import { ACCOUNT } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, @@ -18,7 +19,7 @@ export interface ISavedSearch { id?: Id; name?: Param; description?: Description; - storedQuery?: Blob; + storedQuery?: Filters; creatorId?: Id; deleterId?: Id; createdAt?: DateTimeTimezone | string; @@ -34,7 +35,7 @@ export class SavedSearch extends AbstractModel implements ISavedSearch { @BawPersistAttr public readonly description?: Description; @BawPersistAttr - public readonly storedQuery?: Blob; + public readonly storedQuery?: Filters; public readonly creatorId?: Id; public readonly deleterId?: Id; @BawDateTime() @@ -53,6 +54,6 @@ export class SavedSearch extends AbstractModel implements ISavedSearch { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("SavedSearch viewUrl not implemented."); } } diff --git a/src/app/models/Study.ts b/src/app/models/Study.ts index 7ffd29d3a..9ba7f638e 100644 --- a/src/app/models/Study.ts +++ b/src/app/models/Study.ts @@ -46,6 +46,6 @@ export class Study extends AbstractModel implements IStudy { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("Study viewUrl not implemented."); } } From c9d6dfa23b75cb1d1691e71454d47ea1b9aa2b4d Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 13:02:29 +1000 Subject: [PATCH 17/29] Reduce persisted User keys Relates to the following comments: - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415463080 - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415462962 --- src/app/models/User.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/app/models/User.ts b/src/app/models/User.ts index 19d4ebad8..ae49b30e5 100644 --- a/src/app/models/User.ts +++ b/src/app/models/User.ts @@ -44,6 +44,7 @@ export interface IUser { * A user model. */ export class User extends AbstractModel implements IUser { + // ! Most fields are persisted because model is saved to, and read from, localstorage for the current user public readonly kind: "User" | "SessionUser" = "User"; @BawPersistAttr public readonly id?: Id; @@ -51,15 +52,12 @@ export class User extends AbstractModel implements IUser { public readonly email?: string; @BawPersistAttr public readonly userName?: UserName; - @BawPersistAttr public readonly signInCount?: number; - @BawPersistAttr public readonly failedAttempts?: number; @BawPersistAttr public readonly imageUrls?: ImageURL[]; @BawPersistAttr public readonly preferences?: any; - @BawPersistAttr public readonly isConfirmed?: boolean; @BawPersistAttr public readonly rolesMask?: number; @@ -67,23 +65,23 @@ export class User extends AbstractModel implements IUser { public readonly rolesMaskNames?: string[]; @BawPersistAttr public readonly timezoneInformation?: TimezoneInformation; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly resetPasswordSentAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly rememberCreatedAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly currentSignInAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly lastSignInAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly confirmedAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly confirmationSentAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly lockedAt?: DateTimeTimezone; @BawDateTime({ persist: true }) public readonly createdAt?: DateTimeTimezone; - @BawDateTime({ persist: true }) + @BawDateTime() public readonly updatedAt?: DateTimeTimezone; @BawDateTime({ persist: true }) public readonly lastSeenAt?: DateTimeTimezone; @@ -94,7 +92,7 @@ export class User extends AbstractModel implements IUser { this.userName = user.userName || "Deleted User"; this.imageUrls = user.imageUrls ? user.imageUrls.map((imageUrl) => { - // TODO Add /assets by default from the API so this check doesn't need to occur + // TODO https://github.com/QutEcoacoustics/baw-server/issues/452 // Default values from API need to have /assets prepended if ( imageUrl.url.startsWith("/") && @@ -180,6 +178,7 @@ export interface ISessionUser extends IUser { * A user model for the website user */ export class SessionUser extends User implements ISessionUser { + // ! All fields are persisted because model is saved to, and read from, localstorage public readonly kind: "User" | "SessionUser" = "SessionUser"; @BawPersistAttr public readonly id?: Id; From 8e104be93eff67f8ce3a60c37d2e61a37d060cff Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 13:09:37 +1000 Subject: [PATCH 18/29] Cleanup missing associations --- src/app/models/AudioEventTag.ts | 1 - src/app/models/ProgressEvent.ts | 1 - src/app/models/Question.ts | 2 +- src/app/models/Response.ts | 16 ++++++++++++---- src/app/models/Study.ts | 6 ++++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/app/models/AudioEventTag.ts b/src/app/models/AudioEventTag.ts index 5d91be13f..32ca4e7fe 100644 --- a/src/app/models/AudioEventTag.ts +++ b/src/app/models/AudioEventTag.ts @@ -37,7 +37,6 @@ export class AudioEventTag extends AbstractModel implements IAudioEventTag { public readonly updatedAt?: DateTimeTimezone; // Associations - // TODO Add AudioEvent association @HasOne(TAG, (m: AudioEventTag) => m.tagId) public tag?: Observable; @HasOne(ACCOUNT, (m: AudioEventTag) => m.creatorId) diff --git a/src/app/models/ProgressEvent.ts b/src/app/models/ProgressEvent.ts index 2655995be..0b32b757a 100644 --- a/src/app/models/ProgressEvent.ts +++ b/src/app/models/ProgressEvent.ts @@ -31,7 +31,6 @@ export class ProgressEvent extends AbstractModel implements IProgressEvent { public readonly createdAt?: DateTimeTimezone; // Associations - // TODO Add DatasetItem association\ @HasOne(DATASET_ITEM, (m: ProgressEvent) => m.datasetItemId) public datasetItem?: Observable; @HasOne(ACCOUNT, (m: ProgressEvent) => m.creatorId) diff --git a/src/app/models/Question.ts b/src/app/models/Question.ts index effd5a8ba..45e66264f 100644 --- a/src/app/models/Question.ts +++ b/src/app/models/Question.ts @@ -45,6 +45,6 @@ export class Question extends AbstractModel implements IQuestion { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("Question viewUrl not implemented."); } } diff --git a/src/app/models/Response.ts b/src/app/models/Response.ts index 471c06034..4671f4144 100644 --- a/src/app/models/Response.ts +++ b/src/app/models/Response.ts @@ -1,4 +1,4 @@ -import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { ACCOUNT, DATASET_ITEM, QUESTION, STUDY } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id } from "@interfaces/apiInterfaces"; import { Observable } from "rxjs"; import { @@ -7,7 +7,10 @@ import { BawPersistAttr, HasOne, } from "./AbstractModel"; -import { User } from "./User"; +import type { DatasetItem } from "./DatasetItem"; +import type { Question } from "./Question"; +import type { Study } from "./Study"; +import type { User } from "./User"; export interface IResponse { id?: Id; @@ -36,7 +39,12 @@ export class Response extends AbstractModel implements IResponse { public readonly createdAt?: DateTimeTimezone | string; // Associations - // TODO Add DatasetItem, Question, and Study associations + @HasOne(DATASET_ITEM, (m: Response) => m.datasetItemId) + public datasetItem?: Observable; + @HasOne(QUESTION, (m: Response) => m.questionId) + public question?: Observable; + @HasOne(STUDY, (m: Response) => m.studyId) + public study?: Observable; @HasOne(ACCOUNT, (m: Response) => m.creatorId) public creator?: Observable; @@ -45,6 +53,6 @@ export class Response extends AbstractModel implements IResponse { } public get viewUrl(): string { - return "/BROKEN_LINK"; + throw new Error("Response viewUrl not implemented."); } } diff --git a/src/app/models/Study.ts b/src/app/models/Study.ts index 9ba7f638e..f2559481d 100644 --- a/src/app/models/Study.ts +++ b/src/app/models/Study.ts @@ -1,4 +1,4 @@ -import { ACCOUNT } from "@baw-api/ServiceTokens"; +import { ACCOUNT, DATASET } from "@baw-api/ServiceTokens"; import { DateTimeTimezone, Id, Param } from "@interfaces/apiInterfaces"; import { Observable } from "rxjs"; import { @@ -7,6 +7,7 @@ import { BawPersistAttr, HasOne, } from "./AbstractModel"; +import type { Dataset } from "./Dataset"; import type { User } from "./User"; export interface IStudy { @@ -35,7 +36,8 @@ export class Study extends AbstractModel implements IStudy { public readonly updatedAt?: DateTimeTimezone; // Associations - // TODO Add Dataset association + @HasOne(DATASET, (m: Study) => m.datasetId) + public dataset?: Observable; @HasOne(ACCOUNT, (m: Study) => m.creatorId) public creator?: Observable; @HasOne(ACCOUNT, (m: Study) => m.updaterId) From 7e11dad2ebfa8c4b43d1e2e0c1006890cc8c4b9b Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 15:07:57 +1000 Subject: [PATCH 19/29] Cleanup AbstractModel test files - https://github.com/QutEcoacoustics/workbench-client/pull/223#discussion_r415464693 - https://github.com/QutEcoacoustics/workbench-client/pull/223#discussion_r415464945 --- src/app/models/AbstractModel.spec.ts | 608 +----------------- src/app/models/AssociationDecorators.spec.ts | 338 ++++++++++ src/app/models/AttributeDecorators.spec.ts | 265 ++++++++ .../baw-api/tests/security.service.spec.ts | 3 +- 4 files changed, 608 insertions(+), 606 deletions(-) create mode 100644 src/app/models/AssociationDecorators.spec.ts create mode 100644 src/app/models/AttributeDecorators.spec.ts diff --git a/src/app/models/AbstractModel.spec.ts b/src/app/models/AbstractModel.spec.ts index 17e6de956..6f5e33dca 100644 --- a/src/app/models/AbstractModel.spec.ts +++ b/src/app/models/AbstractModel.spec.ts @@ -1,29 +1,5 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { Injector } from "@angular/core"; -import { async, fakeAsync, TestBed, tick } from "@angular/core/testing"; -import { ApiErrorDetails } from "@baw-api/api.interceptor.service"; -import { MockModel as ChildModel } from "@baw-api/mock/baseApiMock.service"; -import { - MOCK, - MockStandardApiService, -} from "@baw-api/mock/standardApiMock.service"; -import { - shouldNotFail, - shouldNotSucceed, -} from "@baw-api/tests/baw-api.service.spec"; -import { Id, Ids } from "@interfaces/apiInterfaces"; import { DateTime, Duration } from "luxon"; -import { BehaviorSubject, Observable, Subject } from "rxjs"; -import { testBawServices } from "../test/helpers/testbed"; -import { - AbstractModel, - BawCollection, - BawDateTime, - BawDuration, - BawPersistAttr, - HasMany, - HasOne, -} from "./AbstractModel"; +import { AbstractModel } from "./AbstractModel"; describe("AbstractModel", () => { describe("toJSON", () => { @@ -87,12 +63,13 @@ describe("AbstractModel", () => { }); it("should handle Duration", () => { - const duration = Duration.fromMillis(100000); + const seconds = 100; + const duration = Duration.fromMillis(seconds * 1000); const model = createModel(["duration"], { id: 1, duration, }); - expect(model.toJSON()).toEqual({ duration: 100 }); + expect(model.toJSON()).toEqual({ duration: seconds }); }); it("should handle multiple", () => { @@ -146,580 +123,3 @@ describe("AbstractModel", () => { }); }); }); - -describe("Attribute Decorators", () => { - describe("BawPersistAttr", () => { - it("should append key to model attributes", () => { - class MockModel extends AbstractModel { - @BawPersistAttr - public readonly name: string; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({}); - expect(model["_attributes"]).toEqual(["name"]); - }); - - it("should append multiple keys to model attributes", () => { - class MockModel extends AbstractModel { - @BawPersistAttr - public readonly name: string; - @BawPersistAttr - public readonly value: number; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({}); - expect(model["_attributes"]).toEqual(["name", "value"]); - }); - - it("should output keys in model toJSON", () => { - class MockModel extends AbstractModel { - @BawPersistAttr - public readonly name: string; - @BawPersistAttr - public readonly value: number; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ name: "test", value: 1 }); - expect(model.toJSON()).toEqual({ name: "test", value: 1 }); - }); - }); - - describe("BawCollection", () => { - function createModel(data: Id[]) { - class MockModel extends AbstractModel { - @BawCollection() - public readonly ids: Ids; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - return new MockModel(data === undefined ? {} : { ids: data }); - } - - it("should handle persist option", () => { - class MockModel extends AbstractModel { - @BawCollection({ persist: true }) - public readonly name: Ids; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ ids: [1, 2, 3] }); - expect(model["_attributes"]).toEqual(["name"]); - }); - - it("should handle override key option", () => { - class MockModel extends AbstractModel { - @BawCollection({ key: "siteIds" }) - public readonly sites: Ids; - public readonly siteIds: Id[]; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ siteIds: [1, 2, 3] }); - expect(model.sites).toEqual(new Set([1, 2, 3])); - expect(model.siteIds).toEqual([1, 2, 3]); - }); - - it("should convert undefined", () => { - const model = createModel(undefined); - expect(model.ids).toEqual(new Set([])); - }); - - it("should convert null", () => { - const model = createModel(null); - expect(model.ids).toEqual(new Set([])); - }); - - it("should convert empty array", () => { - const model = createModel([]); - expect(model.ids).toEqual(new Set([])); - }); - - it("should convert single value array", () => { - const model = createModel([1]); - expect(model.ids).toEqual(new Set([1])); - }); - - it("should convert multi value array", () => { - const model = createModel([1, 2, 3]); - expect(model.ids).toEqual(new Set([1, 2, 3])); - }); - }); - - describe("BawDateTime", () => { - function createModel(data: string) { - class MockModel extends AbstractModel { - @BawDateTime() - public readonly date: DateTime; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - return new MockModel(data === undefined ? {} : { date: data }); - } - - it("should handle persist option", () => { - class MockModel extends AbstractModel { - @BawDateTime({ persist: true }) - public readonly date: DateTime; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ date: "2019-01-01T00:00:00" }); - expect(model["_attributes"]).toEqual(["date"]); - }); - - it("should handle override key option", () => { - class MockModel extends AbstractModel { - @BawDateTime({ key: "timestamp" }) - public readonly dateTime: DateTime; - public readonly timestamp: string; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ timestamp: "2019-01-01T00:00:00" }); - expect(model.dateTime).toEqual(DateTime.fromISO("2019-01-01T00:00:00")); - expect(model.timestamp).toEqual("2019-01-01T00:00:00"); - }); - - it("should convert undefined", () => { - const model = createModel(undefined); - expect(model.date).toEqual(null); - }); - - it("should convert null", () => { - const model = createModel(null); - expect(model.date).toEqual(null); - }); - - it("should convert timestamp string", () => { - const model = createModel("2019-01-01T00:00:00"); - expect(model.date).toEqual(DateTime.fromISO("2019-01-01T00:00:00")); - }); - }); - - describe("BawDuration", () => { - const defaultSeconds = 100; - let defaultDuration: Duration; - - function createModel(data: number) { - class MockModel extends AbstractModel { - @BawDuration() - public readonly duration: Duration; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - return new MockModel(data === undefined ? {} : { duration: data }); - } - - beforeEach(() => { - defaultDuration = Duration.fromObject({ - years: 0, - months: 0, - days: 0, - hours: 0, - minutes: 1, - seconds: 40, - }); - }); - - it("should handle persist option", () => { - class MockModel extends AbstractModel { - @BawDuration({ persist: true }) - public readonly duration: Duration; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ - duration: defaultSeconds, - }); - expect(model["_attributes"]).toEqual(["duration"]); - }); - - it("should handle override key option", () => { - class MockModel extends AbstractModel { - @BawDuration({ key: "seconds" }) - public readonly duration: Duration; - public readonly seconds: number; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - const model = new MockModel({ seconds: defaultSeconds }); - expect(model.duration).toEqual(defaultDuration); - expect(model.seconds).toEqual(defaultSeconds); - }); - - it("should convert undefined", () => { - const model = createModel(undefined); - expect(model.duration).toEqual(null); - }); - - it("should convert null", () => { - const model = createModel(null); - expect(model.duration).toEqual(null); - }); - - it("should convert duration string", () => { - const model = createModel(defaultSeconds); - expect(model.duration).toEqual(defaultDuration); - }); - }); -}); - -describe("Association Decorators", () => { - let injector: Injector; - let api: MockStandardApiService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - ...testBawServices, - MockStandardApiService, - { provide: MOCK.token, useExisting: MockStandardApiService }, - ], - }); - - api = TestBed.inject(MockStandardApiService); - injector = TestBed.inject(Injector); - })); - - describe("HasMany", () => { - function createModel( - data: object, - modelInjector: Injector, - key?: string, - ...modelParameters: ((target) => Id)[] - ) { - class MockModel extends AbstractModel { - public readonly ids: Ids; - public readonly param1: Id; - public readonly param2: Id; - @HasMany(MOCK, (m: MockModel) => m.ids, key, ...modelParameters) - public readonly childModels: Observable; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - return new MockModel(data, modelInjector); - } - - function interceptApiRequest( - models?: ChildModel[], - error?: ApiErrorDetails - ) { - spyOn(api, "filter").and.callFake(() => { - if (!error) { - return new BehaviorSubject(models); - } else { - const subject = new Subject(); - subject.error(error); - return subject; - } - }); - } - - it("should handle undefined modelIdentifier", (done) => { - const model = createModel({ ids: undefined }, injector); - model.childModels.subscribe((models) => { - expect(models).toEqual([]); - done(); - }, shouldNotFail); - }); - - [ - { - type: "Set", - empty: new Set([]), - single: new Set([1]), - multiple: new Set([1, 2]), - }, - { type: "Array", empty: [], single: [1], multiple: [1, 2] }, - ].forEach((idsType) => { - describe(idsType.type, () => { - it("should handle empty", (done) => { - interceptApiRequest([]); - const model = createModel({ ids: idsType.empty }, injector); - model.childModels.subscribe((models) => { - expect(models).toEqual([]); - done(); - }, shouldNotFail); - }); - - it("should handle single modelIdentifier", (done) => { - interceptApiRequest([new ChildModel({ id: 1 })]); - const model = createModel({ ids: idsType.single }, injector); - model.childModels.subscribe((models) => { - expect(models).toEqual([new ChildModel({ id: 1 })]); - done(); - }, shouldNotFail); - }); - - it("should handle multiple modelIdentifiers", (done) => { - const response = [ - new ChildModel({ id: 1 }), - new ChildModel({ id: 2 }), - ]; - interceptApiRequest(response); - const model = createModel({ ids: idsType.multiple }, injector); - model.childModels.subscribe((models) => { - expect(models).toEqual(response); - done(); - }, shouldNotFail); - }); - - it("should handle single parameter", () => { - interceptApiRequest([]); - const model = createModel( - { ids: idsType.multiple, param1: 5 }, - injector, - undefined, - (target) => target.param1 - ); - model.childModels.subscribe(); - expect(api.filter).toHaveBeenCalledWith( - { - filter: { id: { in: [1, 2] } }, - }, - [5] - ); - }); - - it("should handle multiple parameters", () => { - interceptApiRequest([]); - const model = createModel( - { ids: idsType.multiple, param1: 5, param2: 10 }, - injector, - undefined, - (target) => target.param1, - (target) => target.param2 - ); - model.childModels.subscribe(); - expect(api.filter).toHaveBeenCalledWith( - { - filter: { id: { in: [1, 2] } }, - }, - [5, 10] - ); - }); - - it("should handle error", (done) => { - interceptApiRequest(undefined, { - status: 401, - message: "Unauthorized", - }); - const model = createModel({ ids: idsType.empty }, injector); - model.childModels.subscribe(shouldNotSucceed, (error) => { - expect(error).toEqual({ status: 401, message: "Unauthorized" }); - done(); - }); - }); - - it("should handle default primary key", () => { - interceptApiRequest([]); - const model = createModel({ ids: idsType.multiple }, injector); - model.childModels.subscribe(); - expect(api.filter).toHaveBeenCalledWith( - { - filter: { id: { in: [1, 2] } }, - }, - [] - ); - }); - - it("should handle custom primary key", () => { - interceptApiRequest([]); - const model = createModel( - { ids: idsType.multiple }, - injector, - "customKey" - ); - model.childModels.subscribe(); - expect(api.filter).toHaveBeenCalledWith( - { - filter: { customKey: { in: [1, 2] } }, - }, - [] - ); - }); - - it("should load cached data", fakeAsync(() => { - spyOn(api, "filter").and.callFake(() => { - const subject = new Subject(); - setTimeout(() => { - subject.next([new ChildModel({ id: 1 })]); - }, 100); - return subject; - }); - - const model = createModel({ ids: idsType.single }, injector); - for (let i = 0; i < 5; i++) { - model.childModels.subscribe((models) => { - expect(models).toEqual([new ChildModel({ id: 1 })]); - }, shouldNotFail); - } - - tick(100); - expect(api.filter).toHaveBeenCalledTimes(1); - })); - }); - }); - }); - - describe("HasOne", () => { - function createModel( - data: object, - modelInjector: Injector, - ...modelParameters: ((target) => Id)[] - ) { - class MockModel extends AbstractModel { - public readonly id: Id; - public readonly param1: Id; - public readonly param2: Id; - @HasOne(MOCK, (m: MockModel) => m.id, ...modelParameters) - public readonly childModel: Observable; - - public get viewUrl(): string { - throw new Error("Method not implemented."); - } - } - - return new MockModel(data, modelInjector); - } - - function interceptApiRequest(model?: ChildModel, error?: ApiErrorDetails) { - spyOn(api, "show").and.callFake(() => { - if (!error) { - return new BehaviorSubject(model); - } else { - const subject = new Subject(); - subject.error(error); - return subject; - } - }); - } - - it("should handle undefined modelIdentifier", (done) => { - const model = createModel({ id: undefined }, injector); - model.childModel.subscribe((response) => { - expect(response).toBe(null); - done(); - }, shouldNotFail); - }); - - it("should handle response", (done) => { - interceptApiRequest(new ChildModel({ id: 1 })); - const model = createModel({ id: 1 }, injector); - model.childModel.subscribe((response) => { - expect(response).toEqual(new ChildModel({ id: 1 })); - done(); - }, shouldNotFail); - }); - - it("should handle single parameter", () => { - interceptApiRequest(new ChildModel({ id: 1 })); - const model = createModel( - { id: 1, param1: 5 }, - injector, - (target) => target.param1 - ); - model.childModel.subscribe(); - expect(api.show).toHaveBeenCalledWith(1, [5]); - }); - - it("should handle multiple parameters", () => { - interceptApiRequest(new ChildModel({ id: 1 })); - const model = createModel( - { id: 1, param1: 5, param2: 10 }, - injector, - (target) => target.param1, - (target) => target.param2 - ); - model.childModel.subscribe(); - expect(api.show).toHaveBeenCalledWith(1, [5, 10]); - }); - - it("should handle undefined modelParameter", () => { - interceptApiRequest(new ChildModel({ id: 1 })); - const model = createModel( - { id: 1, param1: 5 }, - injector, - (target) => target.param1, - (target) => target.param2 - ); - model.childModel.subscribe(); - expect(api.show).toHaveBeenCalledWith(1, [5, undefined]); - }); - - it("should handle error", (done) => { - interceptApiRequest(undefined, { - status: 401, - message: "Unauthorized", - }); - const model = createModel({ id: 1 }, injector); - model.childModel.subscribe(shouldNotSucceed, (error) => { - expect(error).toEqual({ status: 401, message: "Unauthorized" }); - done(); - }); - }); - - it("should load cached data", fakeAsync(() => { - spyOn(api, "show").and.callFake(() => { - const subject = new Subject(); - setTimeout(() => { - subject.next(new ChildModel({ id: 1 })); - }, 100); - return subject; - }); - - const model = createModel({ id: 1 }, injector); - for (let i = 0; i < 5; i++) { - model.childModel.subscribe((models) => { - expect(models).toEqual(new ChildModel({ id: 1 })); - }, shouldNotFail); - } - - tick(100); - expect(api.show).toHaveBeenCalledTimes(1); - })); - }); -}); diff --git a/src/app/models/AssociationDecorators.spec.ts b/src/app/models/AssociationDecorators.spec.ts new file mode 100644 index 000000000..62ea78344 --- /dev/null +++ b/src/app/models/AssociationDecorators.spec.ts @@ -0,0 +1,338 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { Injector } from "@angular/core"; +import { async, fakeAsync, TestBed, tick } from "@angular/core/testing"; +import { ApiErrorDetails } from "@baw-api/api.interceptor.service"; +import { MockModel as ChildModel } from "@baw-api/mock/baseApiMock.service"; +import { + MOCK, + MockStandardApiService, +} from "@baw-api/mock/standardApiMock.service"; +import { + shouldNotFail, + shouldNotSucceed, +} from "@baw-api/tests/baw-api.service.spec"; +import { Id, Ids } from "@interfaces/apiInterfaces"; +import { BehaviorSubject, Observable, Subject } from "rxjs"; +import { testBawServices } from "../test/helpers/testbed"; +import { AbstractModel, HasMany, HasOne } from "./AbstractModel"; + +describe("Association Decorators", () => { + let injector: Injector; + let api: MockStandardApiService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + ...testBawServices, + MockStandardApiService, + { provide: MOCK.token, useExisting: MockStandardApiService }, + ], + }); + + api = TestBed.inject(MockStandardApiService); + injector = TestBed.inject(Injector); + })); + + describe("HasMany", () => { + function createModel( + data: object, + modelInjector: Injector, + key?: string, + ...modelParameters: ((target) => Id)[] + ) { + class MockModel extends AbstractModel { + public readonly ids: Ids; + public readonly param1: Id; + public readonly param2: Id; + @HasMany(MOCK, (m: MockModel) => m.ids, key, ...modelParameters) + public readonly childModels: Observable; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + return new MockModel(data, modelInjector); + } + + function interceptApiRequest( + models?: ChildModel[], + error?: ApiErrorDetails + ) { + spyOn(api, "filter").and.callFake(() => { + if (!error) { + return new BehaviorSubject(models); + } else { + const subject = new Subject(); + subject.error(error); + return subject; + } + }); + } + + it("should handle undefined modelIdentifier", (done) => { + const model = createModel({ ids: undefined }, injector); + model.childModels.subscribe((models) => { + expect(models).toEqual([]); + done(); + }, shouldNotFail); + }); + + [ + { + type: "Set", + empty: new Set([]), + single: new Set([1]), + multiple: new Set([1, 2]), + }, + { type: "Array", empty: [], single: [1], multiple: [1, 2] }, + ].forEach((idsType) => { + describe(idsType.type, () => { + it("should handle empty", (done) => { + interceptApiRequest([]); + const model = createModel({ ids: idsType.empty }, injector); + model.childModels.subscribe((models) => { + expect(models).toEqual([]); + done(); + }, shouldNotFail); + }); + + it("should handle single modelIdentifier", (done) => { + interceptApiRequest([new ChildModel({ id: 1 })]); + const model = createModel({ ids: idsType.single }, injector); + model.childModels.subscribe((models) => { + expect(models).toEqual([new ChildModel({ id: 1 })]); + done(); + }, shouldNotFail); + }); + + it("should handle multiple modelIdentifiers", (done) => { + const response = [ + new ChildModel({ id: 1 }), + new ChildModel({ id: 2 }), + ]; + interceptApiRequest(response); + const model = createModel({ ids: idsType.multiple }, injector); + model.childModels.subscribe((models) => { + expect(models).toEqual(response); + done(); + }, shouldNotFail); + }); + + it("should handle single parameter", () => { + interceptApiRequest([]); + const model = createModel( + { ids: idsType.multiple, param1: 5 }, + injector, + undefined, + (target) => target.param1 + ); + model.childModels.subscribe(); + expect(api.filter).toHaveBeenCalledWith( + { + filter: { id: { in: [1, 2] } }, + }, + [5] + ); + }); + + it("should handle multiple parameters", () => { + interceptApiRequest([]); + const model = createModel( + { ids: idsType.multiple, param1: 5, param2: 10 }, + injector, + undefined, + (target) => target.param1, + (target) => target.param2 + ); + model.childModels.subscribe(); + expect(api.filter).toHaveBeenCalledWith( + { + filter: { id: { in: [1, 2] } }, + }, + [5, 10] + ); + }); + + it("should handle error", (done) => { + interceptApiRequest(undefined, { + status: 401, + message: "Unauthorized", + }); + const model = createModel({ ids: idsType.empty }, injector); + model.childModels.subscribe(shouldNotSucceed, (error) => { + expect(error).toEqual({ status: 401, message: "Unauthorized" }); + done(); + }); + }); + + it("should handle default primary key", () => { + interceptApiRequest([]); + const model = createModel({ ids: idsType.multiple }, injector); + model.childModels.subscribe(); + expect(api.filter).toHaveBeenCalledWith( + { + filter: { id: { in: [1, 2] } }, + }, + [] + ); + }); + + it("should handle custom primary key", () => { + interceptApiRequest([]); + const model = createModel( + { ids: idsType.multiple }, + injector, + "customKey" + ); + model.childModels.subscribe(); + expect(api.filter).toHaveBeenCalledWith( + { + filter: { customKey: { in: [1, 2] } }, + }, + [] + ); + }); + + it("should load cached data", fakeAsync(() => { + spyOn(api, "filter").and.callFake(() => { + const subject = new Subject(); + setTimeout(() => { + subject.next([new ChildModel({ id: 1 })]); + }, 100); + return subject; + }); + + const model = createModel({ ids: idsType.single }, injector); + for (let i = 0; i < 5; i++) { + model.childModels.subscribe((models) => { + expect(models).toEqual([new ChildModel({ id: 1 })]); + }, shouldNotFail); + } + + tick(100); + expect(api.filter).toHaveBeenCalledTimes(1); + })); + }); + }); + }); + + describe("HasOne", () => { + function createModel( + data: object, + modelInjector: Injector, + ...modelParameters: ((target) => Id)[] + ) { + class MockModel extends AbstractModel { + public readonly id: Id; + public readonly param1: Id; + public readonly param2: Id; + @HasOne(MOCK, (m: MockModel) => m.id, ...modelParameters) + public readonly childModel: Observable; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + return new MockModel(data, modelInjector); + } + + function interceptApiRequest(model?: ChildModel, error?: ApiErrorDetails) { + spyOn(api, "show").and.callFake(() => { + if (!error) { + return new BehaviorSubject(model); + } else { + const subject = new Subject(); + subject.error(error); + return subject; + } + }); + } + + it("should handle undefined modelIdentifier", (done) => { + const model = createModel({ id: undefined }, injector); + model.childModel.subscribe((response) => { + expect(response).toBe(null); + done(); + }, shouldNotFail); + }); + + it("should handle response", (done) => { + interceptApiRequest(new ChildModel({ id: 1 })); + const model = createModel({ id: 1 }, injector); + model.childModel.subscribe((response) => { + expect(response).toEqual(new ChildModel({ id: 1 })); + done(); + }, shouldNotFail); + }); + + it("should handle single parameter", () => { + interceptApiRequest(new ChildModel({ id: 1 })); + const model = createModel( + { id: 1, param1: 5 }, + injector, + (target) => target.param1 + ); + model.childModel.subscribe(); + expect(api.show).toHaveBeenCalledWith(1, [5]); + }); + + it("should handle multiple parameters", () => { + interceptApiRequest(new ChildModel({ id: 1 })); + const model = createModel( + { id: 1, param1: 5, param2: 10 }, + injector, + (target) => target.param1, + (target) => target.param2 + ); + model.childModel.subscribe(); + expect(api.show).toHaveBeenCalledWith(1, [5, 10]); + }); + + it("should handle undefined modelParameter", () => { + interceptApiRequest(new ChildModel({ id: 1 })); + const model = createModel( + { id: 1, param1: 5 }, + injector, + (target) => target.param1, + (target) => target.param2 + ); + model.childModel.subscribe(); + expect(api.show).toHaveBeenCalledWith(1, [5, undefined]); + }); + + it("should handle error", (done) => { + interceptApiRequest(undefined, { + status: 401, + message: "Unauthorized", + }); + const model = createModel({ id: 1 }, injector); + model.childModel.subscribe(shouldNotSucceed, (error) => { + expect(error).toEqual({ status: 401, message: "Unauthorized" }); + done(); + }); + }); + + it("should load cached data", fakeAsync(() => { + spyOn(api, "show").and.callFake(() => { + const subject = new Subject(); + setTimeout(() => { + subject.next(new ChildModel({ id: 1 })); + }, 100); + return subject; + }); + + const model = createModel({ id: 1 }, injector); + for (let i = 0; i < 5; i++) { + model.childModel.subscribe((models) => { + expect(models).toEqual(new ChildModel({ id: 1 })); + }, shouldNotFail); + } + + tick(100); + expect(api.show).toHaveBeenCalledTimes(1); + })); + }); +}); diff --git a/src/app/models/AttributeDecorators.spec.ts b/src/app/models/AttributeDecorators.spec.ts new file mode 100644 index 000000000..064988d8d --- /dev/null +++ b/src/app/models/AttributeDecorators.spec.ts @@ -0,0 +1,265 @@ +import { Id, Ids } from "@interfaces/apiInterfaces"; +import { DateTime, Duration } from "luxon"; +import { + AbstractModel, + BawCollection, + BawDateTime, + BawDuration, + BawPersistAttr, +} from "./AbstractModel"; + +describe("Attribute Decorators", () => { + describe("BawPersistAttr", () => { + it("should append key to model attributes", () => { + class MockModel extends AbstractModel { + @BawPersistAttr + public readonly name: string; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({}); + expect(model["_attributes"]).toEqual(["name"]); + }); + + it("should append multiple keys to model attributes", () => { + class MockModel extends AbstractModel { + @BawPersistAttr + public readonly name: string; + @BawPersistAttr + public readonly value: number; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({}); + expect(model["_attributes"]).toEqual(["name", "value"]); + }); + + it("should output keys in model toJSON", () => { + class MockModel extends AbstractModel { + @BawPersistAttr + public readonly name: string; + @BawPersistAttr + public readonly value: number; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ name: "test", value: 1 }); + expect(model.toJSON()).toEqual({ name: "test", value: 1 }); + }); + }); + + describe("BawCollection", () => { + function createModel(data: Id[]) { + class MockModel extends AbstractModel { + @BawCollection() + public readonly ids: Ids; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + return new MockModel(data === undefined ? {} : { ids: data }); + } + + it("should handle persist option", () => { + class MockModel extends AbstractModel { + @BawCollection({ persist: true }) + public readonly name: Ids; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ ids: [1, 2, 3] }); + expect(model["_attributes"]).toEqual(["name"]); + }); + + it("should handle override key option", () => { + class MockModel extends AbstractModel { + @BawCollection({ key: "siteIds" }) + public readonly sites: Ids; + public readonly siteIds: Id[]; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ siteIds: [1, 2, 3] }); + expect(model.sites).toEqual(new Set([1, 2, 3])); + expect(model.siteIds).toEqual([1, 2, 3]); + }); + + it("should convert undefined", () => { + const model = createModel(undefined); + expect(model.ids).toEqual(new Set([])); + }); + + it("should convert null", () => { + const model = createModel(null); + expect(model.ids).toEqual(new Set([])); + }); + + it("should convert empty array", () => { + const model = createModel([]); + expect(model.ids).toEqual(new Set([])); + }); + + it("should convert single value array", () => { + const model = createModel([1]); + expect(model.ids).toEqual(new Set([1])); + }); + + it("should convert multi value array", () => { + const model = createModel([1, 2, 3]); + expect(model.ids).toEqual(new Set([1, 2, 3])); + }); + }); + + describe("BawDateTime", () => { + function createModel(data: string) { + class MockModel extends AbstractModel { + @BawDateTime() + public readonly date: DateTime; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + return new MockModel(data === undefined ? {} : { date: data }); + } + + it("should handle persist option", () => { + class MockModel extends AbstractModel { + @BawDateTime({ persist: true }) + public readonly date: DateTime; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ date: "2019-01-01T00:00:00" }); + expect(model["_attributes"]).toEqual(["date"]); + }); + + it("should handle override key option", () => { + class MockModel extends AbstractModel { + @BawDateTime({ key: "timestamp" }) + public readonly dateTime: DateTime; + public readonly timestamp: string; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ timestamp: "2019-01-01T00:00:00" }); + expect(model.dateTime).toEqual(DateTime.fromISO("2019-01-01T00:00:00")); + expect(model.timestamp).toEqual("2019-01-01T00:00:00"); + }); + + it("should convert undefined", () => { + const model = createModel(undefined); + expect(model.date).toEqual(null); + }); + + it("should convert null", () => { + const model = createModel(null); + expect(model.date).toEqual(null); + }); + + it("should convert timestamp string", () => { + const model = createModel("2019-01-01T00:00:00"); + expect(model.date).toEqual(DateTime.fromISO("2019-01-01T00:00:00")); + }); + }); + + describe("BawDuration", () => { + const defaultSeconds = 100; + let defaultDuration: Duration; + + function createModel(data: number) { + class MockModel extends AbstractModel { + @BawDuration() + public readonly duration: Duration; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + return new MockModel(data === undefined ? {} : { duration: data }); + } + + beforeEach(() => { + defaultDuration = Duration.fromObject({ + years: 0, + months: 0, + days: 0, + hours: 0, + minutes: 1, + seconds: 40, + }); + }); + + it("should handle persist option", () => { + class MockModel extends AbstractModel { + @BawDuration({ persist: true }) + public readonly duration: Duration; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ + duration: defaultSeconds, + }); + expect(model["_attributes"]).toEqual(["duration"]); + }); + + it("should handle override key option", () => { + class MockModel extends AbstractModel { + @BawDuration({ key: "seconds" }) + public readonly duration: Duration; + public readonly seconds: number; + + public get viewUrl(): string { + throw new Error("Method not implemented."); + } + } + + const model = new MockModel({ seconds: defaultSeconds }); + expect(model.duration).toEqual(defaultDuration); + expect(model.seconds).toEqual(defaultSeconds); + }); + + it("should convert undefined", () => { + const model = createModel(undefined); + expect(model.duration).toEqual(null); + }); + + it("should convert null", () => { + const model = createModel(null); + expect(model.duration).toEqual(null); + }); + + it("should convert duration string", () => { + const model = createModel(defaultSeconds); + expect(model.duration).toEqual(defaultDuration); + }); + }); +}); diff --git a/src/app/services/baw-api/tests/security.service.spec.ts b/src/app/services/baw-api/tests/security.service.spec.ts index 5b7f06f66..933840700 100644 --- a/src/app/services/baw-api/tests/security.service.spec.ts +++ b/src/app/services/baw-api/tests/security.service.spec.ts @@ -162,8 +162,7 @@ describe("SecurityService", () => { it("store user", fakeAsync(() => { const userDetails = { id: 1, - userName: "username", - isConfirmed: false, + userName: "userName", // Purposely wrong to highlight session user overrides user lastSeenAt: "1970-01-01T00:00:00.000+10:00", rolesMask: 2, rolesMaskNames: ["user"], From a8d8b914e22f504a112cabc6e12e90b4ad735d2c Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 15:27:21 +1000 Subject: [PATCH 20/29] Convert model attributes to symbol - https://github.com/QutEcoacoustics/workbench-client/pull/190#discussion_r415462585 - https://github.com/QutEcoacoustics/workbench-client/pull/223#discussion_r415478360 --- src/app/models/AbstractModel.spec.ts | 2 +- src/app/models/AbstractModel.ts | 20 +++++++++++--------- src/app/models/AttributeDecorators.spec.ts | 10 +++++----- src/app/models/Tag.ts | 1 + 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/app/models/AbstractModel.spec.ts b/src/app/models/AbstractModel.spec.ts index 6f5e33dca..2ebdac449 100644 --- a/src/app/models/AbstractModel.spec.ts +++ b/src/app/models/AbstractModel.spec.ts @@ -7,7 +7,7 @@ describe("AbstractModel", () => { class MockModel extends AbstractModel { constructor(modelData: any) { super(modelData); - this["_attributes"] = attributes; + this[AbstractModel.attributeKey] = attributes; } public get viewUrl(): string { diff --git a/src/app/models/AbstractModel.ts b/src/app/models/AbstractModel.ts index c9cdcbd3c..17ce91a82 100644 --- a/src/app/models/AbstractModel.ts +++ b/src/app/models/AbstractModel.ts @@ -20,6 +20,13 @@ export abstract class AbstractModel { */ private static metaKey = Symbol("meta"); + /** + * Hidden attributes symbol. + * This stores the list of model attributes which are used to + * generate the toJSON() output. + */ + public static attributeKey = Symbol("meta"); + /** * Model ID */ @@ -30,11 +37,6 @@ export abstract class AbstractModel { */ public readonly kind: string; - /** - * Model attributes. This is used to generate the toJSON() output of the model. - */ - private _attributes: string[]; - /** * Redirect path to view model on website. This is a string which can be * used by `Router.navigateByUrl()` without any processing. For example, @@ -57,7 +59,7 @@ export abstract class AbstractModel { */ public toJSON() { const output = {}; - this._attributes.forEach((attribute) => { + this[AbstractModel.attributeKey].forEach((attribute) => { const value = this[attribute]; if (value instanceof Set) { output[attribute] = Array.from(value); @@ -243,11 +245,11 @@ function createModelDecorator( * Add key to the models attributes */ export function BawPersistAttr(model: AbstractModel, key: string) { - if (!model["_attributes"]) { - model["_attributes"] = []; + if (!model[AbstractModel.attributeKey]) { + model[AbstractModel.attributeKey] = []; } - model["_attributes"].push(key); + model[AbstractModel.attributeKey].push(key); } /** diff --git a/src/app/models/AttributeDecorators.spec.ts b/src/app/models/AttributeDecorators.spec.ts index 064988d8d..a02a297f9 100644 --- a/src/app/models/AttributeDecorators.spec.ts +++ b/src/app/models/AttributeDecorators.spec.ts @@ -21,7 +21,7 @@ describe("Attribute Decorators", () => { } const model = new MockModel({}); - expect(model["_attributes"]).toEqual(["name"]); + expect(model[AbstractModel.attributeKey]).toEqual(["name"]); }); it("should append multiple keys to model attributes", () => { @@ -37,7 +37,7 @@ describe("Attribute Decorators", () => { } const model = new MockModel({}); - expect(model["_attributes"]).toEqual(["name", "value"]); + expect(model[AbstractModel.attributeKey]).toEqual(["name", "value"]); }); it("should output keys in model toJSON", () => { @@ -82,7 +82,7 @@ describe("Attribute Decorators", () => { } const model = new MockModel({ ids: [1, 2, 3] }); - expect(model["_attributes"]).toEqual(["name"]); + expect(model[AbstractModel.attributeKey]).toEqual(["name"]); }); it("should handle override key option", () => { @@ -152,7 +152,7 @@ describe("Attribute Decorators", () => { } const model = new MockModel({ date: "2019-01-01T00:00:00" }); - expect(model["_attributes"]).toEqual(["date"]); + expect(model[AbstractModel.attributeKey]).toEqual(["date"]); }); it("should handle override key option", () => { @@ -228,7 +228,7 @@ describe("Attribute Decorators", () => { const model = new MockModel({ duration: defaultSeconds, }); - expect(model["_attributes"]).toEqual(["duration"]); + expect(model[AbstractModel.attributeKey]).toEqual(["duration"]); }); it("should handle override key option", () => { diff --git a/src/app/models/Tag.ts b/src/app/models/Tag.ts index 3a021d68d..8831b161f 100644 --- a/src/app/models/Tag.ts +++ b/src/app/models/Tag.ts @@ -38,6 +38,7 @@ export class Tag extends AbstractModel implements ITag { public readonly id?: Id; @BawPersistAttr public readonly text?: string; + // * Count attribute is unlikely to be an API output @BawPersistAttr public readonly count?: number; @BawPersistAttr From cf246c7827ae9e0538906d39ef6f799a2fc18e47 Mon Sep 17 00:00:00 2001 From: Charles Alleman Date: Wed, 29 Apr 2020 20:11:45 +1000 Subject: [PATCH 21/29] Default account associations - https://github.com/QutEcoacoustics/workbench-client/pull/223#discussion_r415506817 --- src/app/models/AbstractModel.ts | 30 ++++++++++++- src/app/models/AnalysisJob.ts | 15 ++++--- src/app/models/AudioEvent.ts | 11 ++--- src/app/models/AudioEventTag.ts | 14 +++--- src/app/models/AudioRecording.ts | 15 ++++--- src/app/models/Bookmark.ts | 8 ++-- src/app/models/Dataset.ts | 8 ++-- src/app/models/DatasetItem.ts | 7 +-- src/app/models/ProgressEvent.ts | 7 +-- src/app/models/Project.ts | 12 ++--- src/app/models/Question.ts | 8 ++-- src/app/models/Response.ts | 7 +-- src/app/models/SavedSearch.ts | 8 ++-- src/app/models/Script.ts | 5 +-- src/app/models/Site.ts | 9 ++-- src/app/models/Study.ts | 12 ++--- src/app/models/Tag.ts | 8 ++-- src/app/models/TagGroup.ts | 7 +-- src/app/services/baw-api/ServiceProviders.ts | 46 ++++++++++---------- src/app/services/baw-api/ServiceTokens.ts | 12 ++++- 20 files changed, 152 insertions(+), 97 deletions(-) diff --git a/src/app/models/AbstractModel.ts b/src/app/models/AbstractModel.ts index 17ce91a82..09b56564e 100644 --- a/src/app/models/AbstractModel.ts +++ b/src/app/models/AbstractModel.ts @@ -1,6 +1,6 @@ import { Injector, Optional } from "@angular/core"; import { ApiFilter, ApiShow, IdOr } from "@baw-api/api-common"; -import { ServiceToken } from "@baw-api/ServiceTokens"; +import { ACCOUNT, ServiceToken } from "@baw-api/ServiceTokens"; import { DateTime, Duration } from "luxon"; import { BehaviorSubject, Observable, of } from "rxjs"; import { map } from "rxjs/operators"; @@ -90,6 +90,34 @@ export abstract class AbstractModel { } } +/** + * Creates an association between the ownerId and its user model + */ +export function Owner() { + return HasOne(ACCOUNT, (m: M) => m.ownerId); +} + +/** + * Creates an association between the creatorId and its user model + */ +export function Creator() { + return HasOne(ACCOUNT, (m: M) => m.creatorId); +} + +/** + * Creates an association between the updaterId and its user model + */ +export function Updater() { + return HasOne(ACCOUNT, (m: M) => m.updaterId); +} + +/** + * Creates an association between the deleterId and its user model + */ +export function Deleter() { + return HasOne(ACCOUNT, (m: M) => m.deleterId); +} + /** * Associate models with list of IDs * @param serviceToken Injection token for API Service diff --git a/src/app/models/AnalysisJob.ts b/src/app/models/AnalysisJob.ts index a0d1a85a3..38c26e62d 100644 --- a/src/app/models/AnalysisJob.ts +++ b/src/app/models/AnalysisJob.ts @@ -1,4 +1,4 @@ -import { ACCOUNT, SAVED_SEARCH, SCRIPT } from "@baw-api/ServiceTokens"; +import { SAVED_SEARCH, SCRIPT } from "@baw-api/ServiceTokens"; import { Duration } from "luxon"; import { Observable } from "rxjs"; import { @@ -12,7 +12,10 @@ import { BawDateTime, BawDuration, BawPersistAttr, + Creator, + Deleter, HasOne, + Updater, } from "./AbstractModel"; import type { SavedSearch } from "./SavedSearch"; import type { Script } from "./Script"; @@ -83,14 +86,14 @@ export class AnalysisJob extends AbstractModel implements IAnalysisJob { public readonly overallDataLengthBytes?: number; // Associations - @HasOne(SCRIPT, (m: AnalysisJob) => m.scriptId) - public script?: Observable