无法使用bindNodeCallback

时间:2017-05-19 13:58:46

标签: angular typescript rxjs auth0

我正在尝试这个:

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
    }));
};

4 个答案:

答案 0 :(得分:1)

我有完全相同的问题。最后,我不是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)

如果你想要对上面的代码进行单元测试,我会使用由rxjs-marbles library开发的惊人@cartant(在上面的评论中回复)。以下是我测试相关部分的一些片段:

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() !我被挂在那个FOREVER
  • 我是marble testing的新手,在我真正理解正在发生的事情之前,我不得不read the manual多次过来

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

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

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

setExact

答案 3 :(得分:0)

您可以通过明确定义参数来实现此目的,就像这样:

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

return fetchProfile$(accessToken);