我还是个初学者,正在构建我第一个用来查询Azure DevOps API以获取测试结果的Angular 仪表板应用程序.
- 查询接口,得到版本定义的过滤列表(大约100个)
- 对于每个版本定义,获取最新版本
- 对于每个版本,获取来自该版本的所有测试运行的集合.
- 收到第一组测试结果后,立即显示所有结果的表(每个发布定义都是一行,测试结果为可展开表).
我设法在可观察对象的嵌套订阅中实现了这一点(见下文),但我知道应该避免这种情况,最好是使用类似mergeMap
/switchMap
和/或forkJoin
这样的内容.几天来一直在和它作斗争,但还没有运气.
然后是挑战#2:第二个数据流应该被添加到其中:管道.遵循相同的方法: for each 最新的管道运行获取一个管道列表,为所有测试运行的每个管道获取一个管道列表.这两个数据流可以/应该分别和异步地获取,一旦其中一个获取了第一组测试运行,它就可以显示在仪表板上.
如何做到这一点??
我的仅使用嵌套订阅的发布定义的有效解决方案:
ngOnInit(): void {
this.router.paramMap.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(params => {
this.teamToFilterOn = params.get('team');
this.apiService.getReleaseDefinitions(this.teamToFilterOn as string)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((releaseDefinitions: any)=> {
if (releaseDefinitions.length === 0) {
this.isLoading = false
}
releaseDefinitions.forEach((releaseDefinition: any) => {
if (releaseDefinition.lastRelease) {
this.apiService.getRelease(releaseDefinition.lastRelease.id)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((info: PipelineOrReleaseInfo) => {
if (info) {
this.apiService.getTestRunsByRelease(info.releaseId)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((testruns: any) => {
this.isLoading = false;
this.results = [...this.results, { info: info, testruns: testruns, totals: this.calculateEnvironmentTotals(testruns.testRunResults)}];
this.dataSource.data = this.results;
});
}
});
}
});
});
});
}
首先try 使用forkJoin
,但不知道如何继续.我也不确定这是否正确,因为forkJoin
似乎要等到两个可观测对象都完成了,但只要其中一个有结果,它就应该继续循环结果并进行剩余的调用.
ngOnInit(): void {
this.router.paramMap.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(params => {
this.teamToFilterOn = params.get('team');
let releaseDefQuery = this.apiService.getReleaseDefinitions(this.teamToFilterOn as string)
let pipelineDefQuery = this.apiService.getPipelineDefinitions(this.teamToFilterOn as string)
forkJoin([releaseDefQuery, pipelineDefQuery]).subscribe(definitions => {
let releaseDefinitions = definitions[0];
let pipelineDefinitions = definitions[1];
releaseDefinitions.forEach((releaseDefinition: any) => {
if (releaseDefinition.lastRelease) {
this.apiService.getRelease(releaseDefinition.lastRelease.id)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((info: PipelineOrReleaseInfo) => {
...
EDIT:为清楚起见,还添加了流水线,产生了相同类型PipelineOrReleaseInfo
和testruns
的对象info
.一旦其中一个流(发布或管道)完成了这两个对象,就可以显示它了.那么,这两个流可以/应该在某个时候合并吗?
pipelineDefinitions.forEach((pipelineDefinition: any) => {
this.apiService.getLatestPipelineRun(pipelineDefinition.id)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((info: PipelineOrReleaseInfo) => {
if (info) {
this.apiService.getTestRunsByPipeline(info.pipelineRunId)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((testruns: any) => {
this.isLoading = false;
this.results = [...this.results, { info: info, testruns: testruns, totals: this.calculateEnvironmentTotals(testruns.testRunResults)}];
this.dataSource.data = this.results;
});
}
});
}
EDIT:完全正常工作的代码来自接受的答案:
ngOnInit(): void {
this.teamToFilterOn = this.router.snapshot.paramMap.get('team');
const releaseResults$: Observable<any> = this.apiService.getReleaseDefinitions(this.teamToFilterOn as string).pipe(
mergeMap(releaseDefs => releaseDefs), // Turns Observable<[]> into Observable<>
filter((releaseDef: any) => releaseDef.lastRelease), // Take only releaseDefs with a lastRelease
mergeMap((releaseDef: any) => this.apiService.getRelease(releaseDef.lastRelease.id)),
filter(releaseInfo => !!releaseInfo), // Continue only when release info is returned
mergeMap((releaseInfo: PipelineOrReleaseInfo) => this.apiService.getTestRunsByRelease(releaseInfo.releaseId)
.pipe(map(testruns => ({ testruns, info: releaseInfo }))))
);
const pipelineResults$: Observable<any> = this.apiService.getPipelineDefinitions(this.teamToFilterOn as string).pipe(
mergeMap(pipelineDefs => pipelineDefs), // Turns Observable<[]> into Observable<def>
mergeMap((pipelineDef: any) => this.apiService.getLastPipelineRun(pipelineDef.id)),
filter(pipelineInfo => !!pipelineInfo), // Continue only when pipeline info is returned
mergeMap((pipelineInfo: PipelineOrReleaseInfo) => this.apiService.getTestRunsByPipeline(pipelineInfo.pipelineRunId)
.pipe(map(testruns => ({ testruns, info: pipelineInfo }))))
);
merge(releaseResults$, pipelineResults$)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(({ testruns, info }) => {
this.isLoading = false;
this.results = [...this.results, { info: info, testruns: testruns, totals: this.calculateEnvironmentTotals(testruns.testRunResults)}];
this.dataSource.data = this.results;
});
}