API: Add API to handle UserInfo.
parent
ac31f7093f
commit
fb738622fe
|
@ -127,3 +127,31 @@ it('Journal Entry sync', async () => {
|
|||
}
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('User info sync', async () => {
|
||||
const userInfoManager = new EteSync.UserInfoManager(credentials, testApiBase);
|
||||
|
||||
// Get when there's nothing
|
||||
await expect(userInfoManager.fetch(USER)).rejects.toBeInstanceOf(EteSync.HTTPError);
|
||||
|
||||
// Create
|
||||
let userInfo = new EteSync.UserInfo();
|
||||
userInfo.deserialize({pubkey: 'dGVzdAo=', content: 'dGVzdAo=', owner: USER});
|
||||
await expect(userInfoManager.create(userInfo)).resolves.not.toBeNull();
|
||||
|
||||
// Get
|
||||
let userInfo2 = await userInfoManager.fetch(USER);
|
||||
expect(userInfo2).not.toBeNull();
|
||||
expect(userInfo.serialize().content).toEqual(userInfo2!.serialize().content);
|
||||
|
||||
// Update
|
||||
userInfo.deserialize({pubkey: 'dGVzdDIK', content: 'dGVzdDIK', owner: USER});
|
||||
await userInfoManager.update(userInfo);
|
||||
userInfo2 = await userInfoManager.fetch(USER);
|
||||
expect(userInfo2).not.toBeNull();
|
||||
expect(userInfo.serialize().content).toEqual(userInfo2!.serialize().content);
|
||||
|
||||
// Delete
|
||||
await userInfoManager.delete(userInfo);
|
||||
await expect(userInfoManager.fetch(USER)).rejects.toBeInstanceOf(EteSync.HTTPError);
|
||||
});
|
||||
|
|
|
@ -65,12 +65,11 @@ export class CollectionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
interface BaseJson {
|
||||
uid: string;
|
||||
interface BaseItemJson {
|
||||
content: base64;
|
||||
}
|
||||
|
||||
class BaseJournal<T extends BaseJson> {
|
||||
class BaseItem<T extends BaseItemJson> {
|
||||
protected _json: T;
|
||||
protected _encrypted: byte[];
|
||||
protected _content?: object;
|
||||
|
@ -85,10 +84,6 @@ class BaseJournal<T extends BaseJson> {
|
|||
this._content = undefined;
|
||||
}
|
||||
|
||||
get uid(): string {
|
||||
return this._json.uid;
|
||||
}
|
||||
|
||||
serialize(): T {
|
||||
return Object.assign(
|
||||
{},
|
||||
|
@ -109,6 +104,16 @@ class BaseJournal<T extends BaseJson> {
|
|||
}
|
||||
}
|
||||
|
||||
interface BaseJson extends BaseItemJson {
|
||||
uid: string;
|
||||
}
|
||||
|
||||
class BaseJournal<T extends BaseJson> extends BaseItem<T> {
|
||||
get uid(): string {
|
||||
return this._json.uid;
|
||||
}
|
||||
}
|
||||
|
||||
export interface JournalJson extends BaseJson {
|
||||
version: number;
|
||||
owner: string;
|
||||
|
@ -219,6 +224,43 @@ export class Entry extends BaseJournal<EntryJson> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface UserInfoJson extends BaseItemJson {
|
||||
version?: number;
|
||||
owner: string;
|
||||
pubkey: base64;
|
||||
}
|
||||
|
||||
export class UserInfo extends BaseItem<UserInfoJson> {
|
||||
constructor(version: number = Constants.CURRENT_VERSION) {
|
||||
super();
|
||||
this._json.version = version;
|
||||
}
|
||||
|
||||
get version(): number {
|
||||
return this._json.version!;
|
||||
}
|
||||
|
||||
get owner() {
|
||||
return this._json.owner;
|
||||
}
|
||||
|
||||
calculateHmac(cryptoManager: CryptoManager, encrypted: byte[]): byte[] {
|
||||
let postfix = stringToByteArray(this._json.pubkey);
|
||||
return cryptoManager.hmac(encrypted.concat(postfix));
|
||||
}
|
||||
|
||||
verify(cryptoManager: CryptoManager) {
|
||||
let calculated = this.calculateHmac(cryptoManager, this.encryptedContent());
|
||||
let hmac = this._encrypted.slice(0, HMAC_SIZE_BYTES);
|
||||
|
||||
super.verifyBase(hmac, calculated);
|
||||
}
|
||||
|
||||
private encryptedContent(): byte[] {
|
||||
return this._encrypted.slice(HMAC_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: baseUrl and apiBase should be the right type all around.
|
||||
|
||||
class BaseNetwork {
|
||||
|
@ -421,3 +463,47 @@ export class EntryManager extends BaseManager {
|
|||
return this.newCall(undefined, extra, apiBase);
|
||||
}
|
||||
}
|
||||
|
||||
export class UserInfoManager extends BaseManager {
|
||||
constructor(credentials: Credentials, apiBase: string) {
|
||||
super(credentials, apiBase, ['user', '']);
|
||||
}
|
||||
|
||||
fetch(owner: string): Promise<UserInfo> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.newCall([owner, '']).then((json: UserInfoJson) => {
|
||||
let userInfo = new UserInfo(json.version);
|
||||
userInfo.deserialize(json);
|
||||
resolve(userInfo);
|
||||
}).catch((error: Error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
create(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'post',
|
||||
body: JSON.stringify(userInfo.serialize()),
|
||||
};
|
||||
|
||||
return this.newCall([], extra);
|
||||
}
|
||||
|
||||
update(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'put',
|
||||
body: JSON.stringify(userInfo.serialize()),
|
||||
};
|
||||
|
||||
return this.newCall([userInfo.owner, ''], extra);
|
||||
}
|
||||
|
||||
delete(userInfo: UserInfo): Promise<{}> {
|
||||
const extra = {
|
||||
method: 'delete',
|
||||
};
|
||||
|
||||
return this.newCall([userInfo.owner, ''], extra);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue