mobx#keys TypeScript Examples

The following examples show how to use mobx#keys. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: accountStore.ts    From lightning-terminal with MIT License 5 votes vote down vote up
/**
   * queries the pool api to fetch the list of accounts and stores them
   * in the state
   */
  async fetchAccounts() {
    this._store.log.info('fetching accounts');

    try {
      const { accountsList } = await this._store.api.pool.listAccounts();
      // also update the node info since accounts rely on current block height
      // to calculate the time/blocks remaining until expiration
      await this._store.nodeStore.fetchInfo();
      runInAction(() => {
        accountsList.forEach(poolAcct => {
          // update existing accounts or create new ones in state. using this
          // approach instead of overwriting the array will cause fewer state
          // mutations, resulting in better react rendering performance
          const traderKey = hex(poolAcct.traderKey);
          const existing = this.accounts.get(traderKey);
          if (existing) {
            existing.update(poolAcct);
          } else {
            this.accounts.set(traderKey, new Account(this._store, poolAcct));
          }
        });
        // remove any accounts in state that are not in the API response
        const serverIds = accountsList.map(a => hex(a.traderKey));
        const localIds = keys(this.accounts).map(key => String(key));
        localIds
          .filter(id => !serverIds.includes(id))
          .forEach(id => this.accounts.delete(id));

        // pre-select the open account with the highest balance
        if (this.sortedAccounts.length) {
          this.setActiveTraderKey(this.sortedAccounts[0].traderKey);
        }

        this._store.log.info('updated accountStore.accounts', toJS(this.accounts));
        this.loaded = true;
      });
    } catch (error) {
      this._store.appView.handleError(error, 'Unable to fetch Accounts');
    }
  }
Example #2
Source File: orderStore.ts    From lightning-terminal with MIT License 5 votes vote down vote up
/**
   * queries the POOL api to fetch the list of orders and stores them
   * in the state
   */
  async fetchOrders() {
    this._store.log.info('fetching orders');

    try {
      const { asksList, bidsList } = await this._store.api.pool.listOrders();
      runInAction(() => {
        const serverIds: string[] = [];

        asksList.forEach(({ details, leaseDurationBlocks }) => {
          const poolOrder = details as POOL.Order.AsObject;
          // update existing orders or create new ones in state. using this
          // approach instead of overwriting the array will cause fewer state
          // mutations, resulting in better react rendering performance
          const nonce = hex(poolOrder.orderNonce);
          const order = this.orders.get(nonce) || new Order(this._store);
          order.update(poolOrder, OrderType.Ask, leaseDurationBlocks);
          this.orders.set(nonce, order);
          serverIds.push(nonce);
        });

        bidsList.forEach(({ details, leaseDurationBlocks, minNodeTier }) => {
          const poolOrder = details as POOL.Order.AsObject;
          // update existing orders or create new ones in state. using this
          // approach instead of overwriting the array will cause fewer state
          // mutations, resulting in better react rendering performance
          const nonce = hex(poolOrder.orderNonce);
          const order = this.orders.get(nonce) || new Order(this._store);
          order.update(poolOrder, OrderType.Bid, leaseDurationBlocks, minNodeTier);
          this.orders.set(nonce, order);
          serverIds.push(nonce);
        });

        // remove any orders in state that are not in the API response
        const localIds = Object.keys(this.orders);
        localIds
          .filter(id => !serverIds.includes(id))
          .forEach(id => this.orders.delete(id));

        this._store.log.info('updated orderStore.orders', toJS(this.orders));
      });

      // fetch leases whenever orders are fetched
      await this.fetchLeases();
    } catch (error) {
      this._store.appView.handleError(error, 'Unable to fetch orders');
    }
  }
Example #3
Source File: orderStore.ts    From lightning-terminal with MIT License 5 votes vote down vote up
/**
   * queries the POOL api to fetch the list of leases and stores them
   * in the state
   */
  async fetchLeases() {
    this._store.log.info('fetching leases');

    try {
      const {
        leasesList,
        totalAmtEarnedSat,
        totalAmtPaidSat,
      } = await this._store.api.pool.listLeases();

      runInAction(() => {
        this.earnedSats = Big(totalAmtEarnedSat);
        this.paidSats = Big(totalAmtPaidSat);
        leasesList.forEach(poolLease => {
          // update existing leases or create new ones in state. using this
          // approach instead of overwriting the array will cause fewer state
          // mutations, resulting in better react rendering performance
          const channelPoint = Lease.channelPointToString(poolLease.channelPoint);
          const existing = this.leases.get(channelPoint);
          if (existing) {
            existing.update(poolLease);
          } else {
            this.leases.set(channelPoint, new Lease(poolLease));
          }
        });
        // remove any leases in state that are not in the API response
        const serverIds = leasesList.map(a => Lease.channelPointToString(a.channelPoint));
        const localIds = keys(this.leases).map(key => String(key));
        localIds
          .filter(id => !serverIds.includes(id))
          .forEach(id => this.leases.delete(id));
        this._store.log.info('updated orderStore.leases', toJS(this.leases));
      });
    } catch (error) {
      this._store.appView.handleError(error, 'Unable to fetch leases');
    }
  }
Example #4
Source File: batchStore.spec.ts    From lightning-terminal with MIT License 4 votes vote down vote up
describe('BatchStore', () => {
  let rootStore: Store;
  let store: BatchStore;
  let index: number;

  beforeEach(() => {
    rootStore = createStore();
    store = rootStore.batchStore;

    // mock the BatchSnapshot response to return a unique id for each batch
    // to avoid overwriting the same record in the store
    index = 0;
    grpcMock.unary.mockImplementation((desc, opts) => {
      let res: any;
      if (desc.methodName === 'BatchSnapshots') {
        const count = (opts.request.toObject() as any).numBatchesBack;
        res = {
          batchesList: [...Array(count)].map((_, i) => ({
            ...poolBatchSnapshot,
            batchId: `${index + i}-${poolBatchSnapshot.batchId}`,
            prevBatchId: `${index + i}-${poolBatchSnapshot.prevBatchId}`,
          })),
        };
        index += BATCH_QUERY_LIMIT;
      } else if (desc.methodName === 'LeaseDurations') {
        res = poolLeaseDurations;
      }
      opts.onEnd({
        status: grpc.Code.OK,
        message: { toObject: () => res },
      } as any);
      return undefined as any;
    });
  });

  it('should fetch batches', async () => {
    expect(store.batches.size).toBe(0);

    await store.fetchBatches();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT);
  });

  it('should append start from the oldest batch when fetching batches multiple times', async () => {
    expect(store.batches.size).toBe(0);

    await store.fetchBatches();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT);

    // calling a second time should append new batches to the list
    await store.fetchBatches();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT * 2);
  });

  it('should handle a number of batches less than the query limit', async () => {
    // mock the BatchSnapshot response to return 5 batches with the last one having a
    // blank prevBatchId to signify that there are no more batches available
    grpcMock.unary.mockImplementation((desc, opts) => {
      let res: any;
      if (desc.methodName === 'BatchSnapshots') {
        res = {
          batchesList: [...Array(5)].map((_, i) => ({
            ...poolBatchSnapshot,
            batchId: b64(`${hex(poolBatchSnapshot.batchId)}0${i}`),
            prevBatchId: i < 4 ? b64(`${hex(poolBatchSnapshot.prevBatchId)}0${i}`) : '',
          })),
        };
        index += BATCH_QUERY_LIMIT;
      } else if (desc.methodName === 'LeaseDurations') {
        res = poolLeaseDurations;
      }
      opts.onEnd({
        status: grpc.Code.OK,
        message: { toObject: () => res },
      } as any);
      return undefined as any;
    });

    expect(store.batches.size).toBe(0);

    await store.fetchBatches();
    expect(store.batches.size).toBe(5);

    await store.fetchBatches();
    expect(store.batches.size).toBe(5);
  });

  it('should handle errors when fetching batches', async () => {
    grpcMock.unary.mockImplementation((desc, opts) => {
      if (desc.methodName === 'BatchSnapshots') {
        throw new Error('test-err');
      }
      opts.onEnd({
        status: grpc.Code.OK,
        message: { toObject: () => poolLeaseDurations },
      } as any);
      return undefined as any;
    });
    expect(rootStore.appView.alerts.size).toBe(0);
    await store.fetchBatches();
    expect(rootStore.appView.alerts.size).toBe(1);
    expect(values(rootStore.appView.alerts)[0].message).toBe('test-err');
  });

  it('should not show error when last snapshot is not found', async () => {
    grpcMock.unary.mockImplementation((desc, opts) => {
      if (desc.methodName === 'BatchSnapshots') {
        throw new Error('batch snapshot not found');
      }
      opts.onEnd({
        status: grpc.Code.OK,
        message: { toObject: () => poolLeaseDurations },
      } as any);
      return undefined as any;
    });
    expect(rootStore.appView.alerts.size).toBe(0);
    await store.fetchBatches();
    expect(rootStore.appView.alerts.size).toBe(0);
  });

  it('should fetch the latest batch', async () => {
    await store.fetchBatches();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT);

    // return the same last batch to ensure no new data is added
    const lastBatchId = store.sortedBatches[0].batchId;
    index--;
    await store.fetchLatestBatch();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT);
    expect(store.sortedBatches[0].batchId).toBe(lastBatchId);

    // return a new batch as the latest
    index = 100;
    await store.fetchLatestBatch();
    expect(store.batches.size).toBe(BATCH_QUERY_LIMIT + 1);
  });

  it('should handle errors when fetching the latest batch', async () => {
    grpcMock.unary.mockImplementationOnce(() => {
      throw new Error('test-err');
    });
    expect(rootStore.appView.alerts.size).toBe(0);
    await store.fetchLatestBatch();
    expect(rootStore.appView.alerts.size).toBe(1);
    expect(values(rootStore.appView.alerts)[0].message).toBe('test-err');
  });

  it('should return the sorted batches', async () => {
    await store.fetchBatches();
    expect(store.sortedBatches[0].batchId).toBe(hex(`0-${poolBatchSnapshot.batchId}`));
    expect(store.sortedBatches[BATCH_QUERY_LIMIT - 1].batchId).toBe(
      hex(`19-${poolBatchSnapshot.batchId}`),
    );

    index = 500;
    await store.fetchLatestBatch();
    expect(store.sortedBatches[0].batchId).toBe(hex(`500-${poolBatchSnapshot.batchId}`));
    expect(store.sortedBatches[BATCH_QUERY_LIMIT].batchId).toBe(
      hex(`19-${poolBatchSnapshot.batchId}`),
    );
  });

  it('should fetch lease durations', async () => {
    expect(store.leaseDurations.size).toBe(0);
    await store.fetchLeaseDurations();
    expect(store.leaseDurations.size).toBe(
      poolLeaseDurations.leaseDurationBucketsMap.length,
    );
    expect(store.selectedLeaseDuration).toBe(
      poolLeaseDurations.leaseDurationBucketsMap[0][0],
    );
  });

  it('should handle errors when fetching lease durations', async () => {
    grpcMock.unary.mockImplementationOnce(() => {
      throw new Error('test-err');
    });
    expect(rootStore.appView.alerts.size).toBe(0);
    await store.fetchLeaseDurations();
    expect(rootStore.appView.alerts.size).toBe(1);
    expect(values(rootStore.appView.alerts)[0].message).toBe('test-err');
  });

  it('should fetch node tier', async () => {
    // return sample data from gRPC requests instead of the batches defined in beforeEach()
    grpcMock.unary.mockImplementation((desc, opts) => {
      const path = `${desc.service.serviceName}.${desc.methodName}`;
      opts.onEnd({
        status: grpc.Code.OK,
        message: { toObject: () => sampleApiResponses[path] },
      } as any);
      return undefined as any;
    });

    await rootStore.nodeStore.fetchInfo();

    expect(store.nodeTier).toBeUndefined();
    await store.fetchNodeTier();
    expect(store.nodeTier).toBe(AUCT.NodeTier.TIER_1);

    // set the pubkey to a random value
    runInAction(() => {
      rootStore.nodeStore.pubkey = 'asdf';
    });
    await store.fetchNodeTier();
    // confirm the tier is set to T0 if the pubkey is not found in the response
    expect(store.nodeTier).toBe(AUCT.NodeTier.TIER_0);
  });

  it('should handle errors when fetching node tier', async () => {
    grpcMock.unary.mockImplementationOnce(() => {
      throw new Error('test-err');
    });
    expect(rootStore.appView.alerts.size).toBe(0);
    await store.fetchNodeTier();
    expect(rootStore.appView.alerts.size).toBe(1);
    expect(values(rootStore.appView.alerts)[0].message).toBe('test-err');
  });

  it('should set the active market', async () => {
    expect(store.selectedLeaseDuration).toBe(0);
    await store.fetchBatches();
    expect(store.selectedLeaseDuration).toBe(2016);
    expect(keys(store.leaseDurations)).toEqual([2016, 4032, 6048, 8064]);
    store.setActiveMarket(4032);
    expect(store.selectedLeaseDuration).toBe(4032);
    store.setActiveMarket(5000);
    expect(store.selectedLeaseDuration).toBe(4032);
    expect(rootStore.appView.alerts.size).toBe(1);
  });

  it('should start and stop polling', async () => {
    let callCount = 0;
    injectIntoGrpcUnary(desc => {
      if (desc.methodName === 'BatchSnapshots') callCount++;
    });

    // allow polling in this test
    Object.defineProperty(config, 'IS_TEST', { get: () => false });
    jest.useFakeTimers();
    jest.spyOn(global, 'setInterval');

    store.startPolling();
    expect(setInterval).toBeCalled();
    expect(callCount).toBe(0);
    // fast forward 1 minute
    jest.advanceTimersByTime(60 * 1000);
    await waitFor(() => {
      expect(callCount).toBe(1);
    });

    jest.spyOn(global, 'clearInterval');
    store.stopPolling();
    expect(clearInterval).toBeCalled();
    // fast forward 1 more minute
    jest.advanceTimersByTime(120 * 1000);
    expect(callCount).toBe(1);

    // revert IS_TEST
    Object.defineProperty(config, 'IS_TEST', { get: () => true });
    jest.useRealTimers();
  });
});