【问题标题】:Unable to use bindNodeCallback无法使用 bindNodeCallback
【发布时间】:2017-10-19 16:46:27
【问题描述】:

我正在尝试这个:

return Observable.bindNodeCallback(this.webAuth.client.userInfo)(this.accessToken);

我正在尝试从 Auth0 userInfo 函数中创建一个 observable,以便我可以在 AuthGuard 的 canActivate 中使用它。

但是我收到了这个错误:

TS2346:Supplied parameters do not match any signature of call target.

上面的库函数是:

Authentication.prototype.userInfo = function(accessToken, cb) {
  var url;
  assert.check(accessToken, {
    type: 'string',
    message: 'accessToken parameter is not valid'
  });
  assert.check(cb, {
    type: 'function',
    message: 'cb parameter is not valid'
  });
  url = urljoin(this.baseOptions.rootUrl, 'userinfo');
  return this.request
    .get(url)
    .set('Authorization', 'Bearer ' + accessToken)
    .end(responseHandler(cb, {
      ignoreCasing: true
    }));
};

【问题讨论】:

  • 你检查了吗:reactivex.io/rxjs/class/es6/…
  • 是的,当然,我相信我正确地遵循了指示!
  • 绝对是。
  • 你的意思是 accessToken 是一个错误对象?
  • 哦!我认为 callback 函数中的第一个参数必须是错误对象:-(

标签: angular typescript rxjs auth0


【解决方案1】:

我知道它是一个旧线程,但我已经能够让我的 auth0 userInfo 方法使用 bindNodeCallback。

如果它可以帮助某人,这是我的解决方案:

// must use the bind() method to change scope of "this" inside the userInfo function
private _userInfo$ = bindNodeCallback(this._Auth0.client.userInfo.bind(this._Auth0.client));

// Get user information
public getUserInfo$() : Observable<any>{

  return this.token$.pipe(
    switchMap(token => this._userInfo$(token))
  );

}

// Check if users email is verified
public checkEmailVerified$() : Observable<boolean>{

  return this.token$.pipe(
    switchMap(token => this._userInfo$(token)),
    map(userInfo => userInfo.email_verified )
  );

}

【讨论】:

    【解决方案2】:

    您可以通过显式定义参数来实现这一点,如下所示:

    const fetchProfile$ = Observable.bindNodeCallback((
      accessToken: string,
      callback: Auth0Callback<Auth0UserProfile>
    ) => this.auth0.client.userInfo(accessToken, callback));
    
    return fetchProfile$(accessToken);
    

    【讨论】:

      【解决方案3】:

      您的问题是原始方法想要访问 this 关键字来访问 Auth0 客户端的内部属性。当您想将其方法包装到 Observable 实例时,您必须绑定此客户端。这是一个例子:

      const userInfo = Observable.bindNodeCallback(this.webAuth.client.userInfo);
      userInfo.call(this, this.accessToken);
      // or
      userInfo.bind(this)(this.accessToken);
      

      毕竟您可以将响应用作 Observable。

      【讨论】:

      • 很好的提示,它让我在解决问题方面取得了进步。谢谢
      【解决方案4】:

      我遇到了完全相同的问题。最终,我不再使用bindNodeCallback(),而是像这样使用discovered you can just wrap the entire call to userInfo() in Observable.create()(注意:外部userInfo() 函数是我的AuthProvider 类的一部分):

      userInfo = (token: string): Observable<User> => Observable.create(observer => {
        this.auth0.client.userInfo(token, (err: any, user: User) => {
          if (err) {
            observer.error(err);
          }
          observer.next(user);
          observer.complete();
        });
      });
      

      测试(使用 Jest)

      如果您想要对上面的代码进行单元测试,我使用@cartant 开发的惊人的rxjs-marbles library(他在上面的 cmets 中做出了回应)。以下是我测试相关部分的一些 sn-ps:

      import { TestBed } from '@angular/core/testing';
      import { marbles } from 'rxjs-marbles/jest'; // <-- add /jest here to level-up!
      import { Observable } from 'rxjs';
      import { StoreModule, Store } from '@ngrx/store';
      import { AuthProvider } from './auth'; // my authentication service
      
      // Here's a mock for the relevant bits of the Auth0 library
      jest.mock('auth0-js', () => ({
        WebAuth: options => ({
          client: {
            userInfo: (token, cb) => { 
              if ( "error" === token ) {
                cb(new Error("Profile error!"), null);
              } else {
                cb(null, {name: "Alice"}); 
              }
            }
          }
        })
      }));
      
      describe("Auth0.WebAuth.client", () => {
        let auth: AuthProvider;
        let store: Store<any>;
      
        beforeEach(() => {
          TestBed.configureTestingModule({
            imports:   [ StoreModule.forRoot({}) ],
            providers: [ AuthProvider ]
          });
          store = TestBed.get(Store);
          spyOn(store, 'pipe');
          auth = TestBed.get(AuthProvider);
        });
      
        it("should return Observable<User> when calling userInfo()", marbles((m) => {
          const user = { name: "Alice" }; // must match mocked value above
          const source = m.cold("--a", { a: "dummy-access-token" });
          const target = m.cold("--b", { b: user });
          const profile = source.flatMap(auth.userInfo);
      
          m.expect(profile).toBeObservable(target);
       }));
      
        it("throw Error on fail when calling userInfo()", marbles((m) => {
          const err = new Error("Profile error!"); // must match mocked value above
          const source = m.cold("--a", { a: "error" }); // this value triggers mock error
          const target = m.cold("--#", null, err);
          const profile = source.flatMap(auth.userInfo);
      
          m.expect(profile).toBeObservable(target);
        }));
      });
      

      我遇到的一些陷阱:

      • 确保在测试中使用 flatMap()!我永远挂断了电话
      • 我是 marble testing 的新手,我必须多次 read the manual 才能真正了解发生的事情

      【讨论】:

        猜你喜欢
        • 2021-04-18
        • 2019-08-24
        • 1970-01-01
        • 2019-08-27
        • 2023-03-27
        • 1970-01-01
        • 1970-01-01
        • 2013-05-20
        • 2020-02-10
        相关资源
        最近更新 更多