Skip to content
This repository has been archived by the owner on Jul 7, 2021. It is now read-only.

Commit

Permalink
feat(profiles): flag contacts with types like wallets (#495)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Faust committed Jun 29, 2020
1 parent 86c4e4f commit 3ddcc5e
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 76 deletions.
5 changes: 1 addition & 4 deletions packages/platform-sdk-profiles/__tests__/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ it("should create a profile with data and persist it when instructed to do so",
const profile = subject.profiles().create("John Doe");

// Create a Contact
profile.contacts().create({
name: "Jane Doe",
addresses: [{ coin: "Ethereum", network: "testnet", address: "TESTNET-ADDRESS" }],
});
profile.contacts().create("Jane Doe");

// Create a Wallet
await profile.wallets().import(identity.mnemonic, ARK, "devnet");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,68 @@
import "jest-extended";
import nock from "nock";

import { ARK } from "@arkecosystem/platform-sdk-ark";

import { ContactRepository } from "../../src/repositories/contact-repository";
import { Profile } from "../../src/profile";
import { Wallet } from "../../src/wallet";
import { container } from "../../src/container";
import { Identifiers } from "../../src/contracts";
import { HttpClient } from "../stubs/client";
import { identity } from "../__fixtures__/identity";

let subject: ContactRepository;

const john = {
name: "John Doe",
addresses: [{ coin: "Bitcoin", network: "livenet", address: "LIVENET-ADDRESS" }],
};
const name = "John Doe";
const addr = { name: "JDB", coin: "ARK", network: "devnet", address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib" };

let walletARK: Wallet;
beforeEach(async () => {
nock.cleanAll();

nock(/.+/)
.get("/api/node/configuration")
.reply(200, require("../__fixtures__/client/configuration.json"))
.get("/api/node/configuration/crypto")
.reply(200, require("../__fixtures__/client/cryptoConfiguration.json"))
.get("/api/node/syncing")
.reply(200, require("../__fixtures__/client/syncing.json"))
.get("/api/wallets/D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib")
.reply(200, require("../__fixtures__/client/wallet.json"))
.persist();

container.set(Identifiers.HttpClient, new HttpClient());

const jane = {
name: "Jane Doe",
addresses: [{ coin: "Ethereum", network: "testnet", address: "TESTNET-ADDRESS" }],
};
const profile = new Profile("profile-id", "John Doe");
walletARK = await profile.wallets().import(identity.mnemonic, ARK, "devnet");

beforeEach(() => {
subject = new ContactRepository(new Profile("profile-id", "John Doe"));
subject = new ContactRepository(profile);

subject.flush();
});

beforeAll(() => nock.disableNetConnect());

test("ContactRepository#create", () => {
expect(subject.keys()).toHaveLength(0);

subject.create(john);
subject.create(name);

expect(subject.keys()).toHaveLength(1);

subject.create(jane);

expect(subject.keys()).toHaveLength(2);
});

test("ContactRepository#find", () => {
expect(() => subject.findById("invalid")).toThrowError("Failed to find");

const contact = subject.create(john);
const contact = subject.create(name);

expect(subject.findById(contact.id())).toBeObject();
});

test("ContactRepository#update", () => {
expect(() => subject.update("invalid", { name: "Jane Doe" })).toThrowError("Failed to find");

const contact = subject.create(john);
const contact = subject.create(name);

subject.update(contact.id(), { name: "Jane Doe" });

Expand All @@ -54,45 +72,38 @@ test("ContactRepository#update", () => {
test("ContactRepository#forget", () => {
expect(() => subject.forget("invalid")).toThrowError("Failed to find");

const contact = subject.create(john);
const contact = subject.create(name);

subject.forget(contact.id());

expect(() => subject.findById(contact.id())).toThrowError("Failed to find");
});

test("ContactRepository#findByAddress", () => {
subject.create(john);
subject.create(jane);
test("ContactRepository#findByAddress", async () => {
const wallet = await (await subject.create(name)).addresses().create(addr);

expect(subject.findByAddress(john.addresses[0].address)).toHaveLength(1);
expect(subject.findByAddress(jane.addresses[0].address)).toHaveLength(1);
expect(subject.findByAddress(wallet.address())).toHaveLength(1);
expect(subject.findByAddress("invalid")).toHaveLength(0);
});

test("ContactRepository#findByCoin", () => {
subject.create(john);
subject.create(jane);
test("ContactRepository#findByCoin", async () => {
const wallet = await (await subject.create(name)).addresses().create(addr);

expect(subject.findByCoin(john.addresses[0].coin)).toHaveLength(1);
expect(subject.findByCoin(jane.addresses[0].coin)).toHaveLength(1);
expect(subject.findByCoin(wallet.coin())).toHaveLength(1);
expect(subject.findByCoin("invalid")).toHaveLength(0);
});

test("ContactRepository#findByNetwork", () => {
subject.create(john);
subject.create(jane);
test("ContactRepository#findByNetwork", async () => {
const wallet = await (await subject.create(name)).addresses().create(addr);

expect(subject.findByNetwork(john.addresses[0].network)).toHaveLength(1);
expect(subject.findByNetwork(jane.addresses[0].network)).toHaveLength(1);
expect(subject.findByNetwork(wallet.network())).toHaveLength(1);
expect(subject.findByNetwork("invalid")).toHaveLength(0);
});

test("ContactRepository#flush", () => {
subject.create(john);
subject.create(jane);
test("ContactRepository#flush", async () => {
const wallet = await (await subject.create(name)).addresses().create(addr);

expect(subject.keys()).toHaveLength(2);
expect(subject.keys()).toHaveLength(1);

subject.flush();

Expand Down
4 changes: 2 additions & 2 deletions packages/platform-sdk-profiles/__tests__/stubs/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import got from "got";

export class HttpClient implements Contracts.HttpClient {
public async get(path: string, searchParams = {}): Promise<Record<string, any>> {
return got.get(path, { searchParams }).json();
return got.get(path, { searchParams, timeout: 1000 }).json();
}

public async post(path: string, body, headers = {}) {
return got.post(path, { body: JSON.stringify(body), headers }).json();
return got.post(path, { body: JSON.stringify(body), headers, timeout: 1000 }).json();
}
}
83 changes: 83 additions & 0 deletions packages/platform-sdk-profiles/src/contact-address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Contracts } from "@arkecosystem/platform-sdk";

import { Avatar } from "./avatar";
import { Profile } from "./profile";
import { Wallet } from "./wallet";

export interface ContactAddressProps {
id: string;
coin: string;
network: string;
name: string;
address: string;
}

export class ContactAddress {
readonly #data: ContactAddressProps;
readonly #wallet: Contracts.WalletData;

private constructor(data: ContactAddressProps, wallet: Contracts.WalletData) {
this.#data = data;
this.#wallet = wallet;
}

public static async make(data: ContactAddressProps, profile: Profile): Promise<ContactAddress> {
const wallet: Wallet = profile.wallets().findByCoinWithNetwork(data.coin, data.network)[0];

if (!wallet) {
throw new Error(`This profile does not contain any wallets for ${data.coin}/${data.network}.`);
}

return new ContactAddress(data, await wallet.wallet(data.address));
}

public id(): string {
return this.#data.id;
}

public coin(): string {
return this.#data.coin;
}

public network(): string {
return this.#data.network;
}

public name(): string {
return this.#data.name;
}

public address(): string {
return this.#data.address;
}

public avatar(): string {
return Avatar.make(this.#data.address);
}

public isDelegate(): boolean {
return this.#wallet.isDelegate();
}

public isKnown(): boolean {
return this.#wallet.isKnown();
}

public isMultiSignature(): boolean {
return this.#wallet.isMultiSignature();
}

public isSecondSignature(): boolean {
return this.#wallet.isSecondSignature();
}

public toObject(): ContactAddressProps {
return {
id: this.id(),
coin: this.coin(),
network: this.network(),
name: this.name(),
address: this.address(),
};
}
}
27 changes: 16 additions & 11 deletions packages/platform-sdk-profiles/src/contact.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Avatar } from "./avatar";
import { ContactAddress, ContactStruct } from "./contracts";
import { ContactStruct } from "./contracts";
import { Profile } from "./profile";
import { ContactAddressRepository } from "./repositories/contact-address-repository";

export class Contact {
#profile: Profile;

#id: string;
#name: string;
#addresses: ContactAddress[] = [];
#addresses: ContactAddressRepository;
#starred: boolean;

public constructor({ id, name, starred, addresses }: ContactStruct, profile: Profile) {
public constructor({ id, name, starred }: ContactStruct, profile: Profile) {
this.#profile = profile;

this.#id = id;
this.#name = name;
this.#starred = starred;
this.#addresses = addresses;

for (const item of this.#addresses) {
item.avatar = Avatar.make(item.address);
}
this.#addresses = new ContactAddressRepository(this.#profile);
}

public async restore(addresses: object[]): Promise<void> {
await this.#addresses.fill(addresses);
}

public id(): string {
Expand All @@ -31,20 +32,24 @@ export class Contact {
return this.#name;
}

public addresses(): ContactAddressRepository {
return this.#addresses;
}

public isStarred(): boolean {
return this.#starred;
}

public addresses(): ContactAddress[] {
return this.#addresses;
public toggleStarred(): void {
this.#starred = !this.isStarred();
}

public toObject(): ContactStruct {
return {
id: this.id(),
name: this.name(),
starred: this.isStarred(),
addresses: this.addresses(),
addresses: this.addresses().toArray(),
};
}
}
6 changes: 3 additions & 3 deletions packages/platform-sdk-profiles/src/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Coins, Contracts } from "@arkecosystem/platform-sdk";

import { ContactAddressProps } from "./contact-address";

export interface EnvironmentOptions {
coins: Record<string, any>;
storage: string | Storage;
Expand Down Expand Up @@ -65,12 +67,10 @@ export interface WalletStruct {
export interface ContactStruct {
id: string;
name: string;
addresses: ContactAddress[];
addresses?: object;
starred: boolean;
}

export type ContactAddress = { coin: string; network: string; address: string; avatar?: string };

// Container Bindings
export const Identifiers = {
AppData: "Data<App>",
Expand Down
19 changes: 9 additions & 10 deletions packages/platform-sdk-profiles/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,15 @@ export class Environment {
object({
id: string().required(),
name: string().required(),
addresses: array()
.of(
object({
coin: string().required(),
network: string().required(),
address: string().required(),
avatar: string().required(),
}).noUnknown(),
)
.required(),
addresses: array().of(
object({
id: string().required(),
coin: string().required(),
network: string().required(),
name: string().required(),
address: string().required(),
}).noUnknown(),
),
starred: boolean().required(),
}).noUnknown(),
),
Expand Down
4 changes: 2 additions & 2 deletions packages/platform-sdk-profiles/src/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { Wallet } from "./wallet";
export class Profile {
#contactRepository!: ContactRepository;
#walletRepository!: WalletRepository;
#notificationRepository!: NotificationRepository;
#dataRepository!: DataRepository;
#notificationRepository!: NotificationRepository;
#settingRepository!: SettingRepository;

#id!: string;
Expand All @@ -25,8 +25,8 @@ export class Profile {
this.#id = id;
this.#name = name;
this.#avatar = Avatar.make(id);
this.#contactRepository = new ContactRepository(this);
this.#walletRepository = new WalletRepository(this);
this.#contactRepository = new ContactRepository(this);
this.#notificationRepository = new NotificationRepository();
this.#dataRepository = new DataRepository();
this.#settingRepository = new SettingRepository(Object.values(ProfileSetting));
Expand Down
Loading

0 comments on commit 3ddcc5e

Please sign in to comment.