Rx JS: проверьте действительность токена перед вызовом S3 List и дождитесь токена, если он недействителен. - PullRequest
1 голос
/ 05 мая 2020

Потерпите меня, так как вопрос длинный и подробный, и я новичок в Rx JS.

Я пытаюсь создать браузер Amazon S3 в Angular, который выглядит например, Windows Explorer.

Примерно так ...

Левый список будет содержать все папки root (и это не будет в виде дерева), и при нажатии на любую папку root вложенные папки и файлы внутри нее будут показаны в правой части окна сведений.

Мне нужен новый токен доступа S3 для каждого из root папок в левом списке. У меня есть серверная служба, которая так делает. Этот токен действителен в течение определенного времени. Таким образом, случаи, в которых текущий токен недействителен, следующие: -

  1. Если пользователь нажимает на какую-либо другую root папку в левом списке.
  2. Если истекает срок действия токена.

Это то, что я написал для управления этим условием истечения срока действия токена: -

private accessTokenSource: BehaviorSubject<AccessToken | null> = new BehaviorSubject(null);
accessToken$ = this.accessTokenSource.asObservable();

getAccessToken() {
    return this.http.get(${this.accessTokenEndpoint}).pipe(
        tap((accessToken) => {
            // set Access token in a subject
            this.accessTokenSource.next(accessToken);
        }),
        switchMapTo(timer(55*60*1000).pipe(
            tap(() => {
                // reset access token in subject since now token is invalid - Expiry case
                this.accessTokenSource.next(null);
            })
        ))
    );
}

// Whoever subscribes to this will fetch the token and start the expiration timer

Поскольку я не хочу раскрывать logi c выборки токена доступа на уровне представления, каждый из мой левый список и компонент деталей вызывает метод getDetails(currentPrefix: string) в s3Service. Этот метод сначала проверяет действительность токена для возможности вызова S3 API, а затем вызывает операцию listObjects и возвращает результат. Вот что у меня есть на данный момент: -

// Checks the validity of token for the current prefix
checkAccessTokenValidity(currentPrefix: string) {
    let isTokenValid: boolean = true;

    // This uses access token set in the subject
    // According to me, it will be reset by the timer's tap operation (when it expires)
    const sub = this.accessToken$.subscribe((token) => {
        // Check token's expiry or usability for current folder and update isTokenValid accordingly
        if(!token || !currentPrefix.includes(token.rootFolder)) {
            isTokenValid = false;
        }
    });
    sub.unsubscribe();

    return isTokenValid;
}


// Public method to call from list and details components
getDetails(currentPrefix: string) {
    const isTokenValid = this.checkAccessTokenValidity(currentPrefix);
    if(!isTokenValid) {
        // This will fetch the token and start the TIMER
        this.getAccessToken().subscribe(() => {});
    }
    // I think that this will not work, since if getAccessToken takes time,
    // then accessToken$ will still be invalid!
    const objectList$ = this.accessToken$.pipe(
        map(token => {
            // S3 List Objects method here, with current token
        })
    )
}

Как решить проблему проверки действительности токена и затем ждать, пока моя служба вернет новый действительный токен, чтобы вызвать S3 API? Любая помощь могла бы быть полезна. Этот подход также может быть совершенно неправильным, поэтому, пожалуйста, не стесняйтесь поправлять и меня.

1 Ответ

1 голос
/ 05 мая 2020

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

Это означает, что эти строки:

const sub = this.accessToken$.subscribe((token) => {
      if(!token || !currentPrefix.includes(token.rootFolder)) {
        isTokenValid = false;
      }
    });
sub.unsubscribe();

могут заменить на

const isTokenValid = !!this.accessTokenSource.value;

Как вы уже упоминали, getAccessToken занимает некоторое время, то есть вы не можете получить результат синхронно.

Быстрое исправление будет следующим:

const tokenValid$ = of(this.accessTokenSource.value); 

const tokenInvalid$ = merge(
  // Not interested in the values emitted as side effects are produced in `tap()`
  // With this, we're just subscribing. This way, an HTTP call will be made
  this.getAccessToken().pipe(ignoreElements()),

  // `accessTokenSource` is a `BehaviorSubject` and we don't want its current value,
  // that's why we're skipping it. Next time it emits, it will have the value returned from `getAccessToken`
  this.accessTokenSource.pipe(skip(1))
);

const objectList$ = iif(() => isTokenValid, tokenValid$, tokenInvalid$);

iff() используется для определения времени подписки , на какое наблюдаемое подписаться.

if(() => booleanValue, subscribeToThisIfTrue, subscribeToThisIfFalse)

Таким образом, когда вы подписываетесь на objectList$, он выберет правильную наблюдаемую, в зависимости от того, действителен ли токен.

...