Угловые рекурсивные HTTP-запросы, объединяющие ответы, в конечном итоге возвращающие одну наблюдаемую - PullRequest
1 голос
/ 04 апреля 2019

Я хотел бы делать рекурсивные HTTP-вызовы API с разбивкой по страницам, пока не пройдусь по всем страницам. Каждая страница содержит массив ресурсов, которые я объединю во временный массив. Прочитав все страницы, я хочу вернуть один массив всех активов в качестве наблюдаемого.

Я использовал трубу с оператором расширения. Я заметил, что смогу распечатать свой окончательный массив после завершения подписки, но я не знаю, как элегантно вернуть этот массив как наблюдаемый. Есть ли способ дождаться завершения одной наблюдаемой и затем вернуть другую?

getAllAssets(sort: string, filter: AssetFilter): Observable<Asset[]> {
  let allAssets: Asset[] = [];
  console.log('getAllAssets()', sort, filter);
  this._getAllAssets(null, null).subscribe((moreAssets) => {
    allAssets = allAssets.concat(moreAssets);
  }, null, () => console.log(allAssets));
  return of(allAssets);
}

_getAllAssets(sort: string, filter: AssetFilter) {
  let currentPage = 0;
  return this.getAssets(null, null, sort, filter).pipe(
    expand(assetsPage => {
      if (assetsPage && assetsPage.page.number + 1 < assetsPage.page.totalPages) {
        return this.getAssets(currentPage += 1, null, sort, filter);
      }
      return empty();
    }),
    map((value) => value._embedded.assets)
  );
}

getAssets(page: number, size: number, sort: string, filter: AssetFilter): Observable<Assets> {
  let url = 'https://gateway.' + environment.region + '.mindsphere.io/api/assetmanagement/v3/assets?';
  if (page) {
    url += 'page=' + page;
  }
  if (size) {
    url += 'size=' + size;
  }
  if (sort) {
    url += 'sort=' + sort;
  }
  if (filter) {
    url += 'filter=' + JSON.stringify(filter);
  }
  return this.http.get<Assets>(url, {
    headers: new HttpHeaders().set('Content-Type', 'application/json').set('Authorization', 'Bearer ' + this.accessToken)
  });
}

Я получаю правильный массив, напечатанный на экране, но я бы хотел вернуть этот массив как наблюдаемый, не создавая наблюдаемую, вернуть наблюдаемую и выдать ее при завершении.

EDIT: Мое исправление должно было использовать toArray() как упомянуто ниже. Затем я просто сделал карту, чтобы превратить Observable<item[][]> в Observable<item[]>

getAllAssets(sort: string, filter: AssetFilter): Observable<Asset[]> {
  let currentPage = 0;
  return this.getAssets(null, null, sort, filter).pipe(
    expand(assetsPage => {
      if (assetsPage && assetsPage.page.number + 1 < assetsPage.page.totalPages) {
        return this.getAssets((currentPage += 1), null, sort, filter);
      }
      return empty();
    }),
    map(value => value._embedded.assets),
    toArray()
  ).pipe(map(assets => [].concat.apply([], assets)));
}

getAssets(page: number, size: number, sort: string, filter: AssetFilter): Observable<Assets> {
  let url = 'https://gateway.' + environment.region + '.mindsphere.io/api/assetmanagement/v3/assets?';
  if (page) {
    url += 'page=' + page;
  }
  if (size) {
    url += 'size=' + size;
  }
  if (sort) {
    url += 'sort=' + sort;
  }
  if (filter) {
    url += 'filter=' + JSON.stringify(filter);
  }
  return this.http.get<Assets>(url, {
    headers: new HttpHeaders().set('Content-Type', 'application/json').set('Authorization', 'Bearer ' + this.accessToken)
  });
}

Ответы [ 2 ]

0 голосов
/ 05 апреля 2019

Это небольшой фрагмент кода, поэтому я собираюсь его сократить и обобщить, так как это общий шаблон, который будет иметь вид:

   _getAllPages(page, constantArgs, lastSet) {
     lastSet = lastSet || []; // deal with entry
     return this.getPage(page, constantArgs).pipe( // fetch the page
        switchMap(res => 
          (res.nextPage) // if nextpage, recurse and concat to last set
             ? this._getAllPages(res.nextPage, lastSet.concat(res.items))
             : of(lastSet.concat(res.items)))); // else break
   }

для целей стиля, который я обычно будусделайте это частным, и у меня будет общедоступный вид:

getAllPages(constantArgs) {
  return this._getAllPages(0, constantArgs);
}

, чтобы сделать API лучше / проще для понимания.

0 голосов
/ 05 апреля 2019

Вы хотите toArray оператор

Вот пример с timer

timer(0, 5).pipe(
    take(3),
    toArray()
  )

toArray

...