Skip to content

A Request Builder and Executor for the Politics and War API

License

Notifications You must be signed in to change notification settings

Rose-PnW/pnw-rexec

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PnW Rexec

A Request Builder and Executor for the Politics and War API

Features

  • Create different profiles for queries.
  • Chain executors to create the behavior you want.
  • Log your requests with a log callback.
  • Paginator results have helper methods.
  • Requests are typed with the fields you request and nothing more.

Sections

Install

npm i pnw-rexec

Usage

import { RequesterProfile } from 'pnw-rexec';

or

const { RequesterProfile } = require('pnw-rexec');

and

const profile = new RequesterProfile().key('my-key');

const { me } = await profile.request().me((me) => me
    .child('nation', (n) => n.fields('nation_name', 'leader_name'))
  )
  .send();
const { leader_name, nation_name } = me.nation;
console.log(`Hello, I'm ${leader_name} of ${nation_name}`);

Executors

You can set up your profiles to use one of the available executors, or write your own.

new RequesterProfile()
  .bin({defer: true, timeout: 1000})
  .cache({cache: true, lifetime: 60_000});

This profile, for example, has three executors chained:

  • An instantaneous executor, that gets created with the profile.
  • A bin executor, that merges together requests of different endpoints.
  • A cache executor, that hashes requests and throws them in a cache.

They are called from the last to the first, so this requester will first look at the cache, then for an empty bin, then execute. You may specify default configurations for each executor, but they can also be overriden in the request itself.

const urgentRequest = await profile.request().send({defer: false});
const unlikelyToChange = await profile.request().send({cache: true, lifetime: 7_200_000})

Logging

You can specify a log callback, that gets a Date object, a query string and a node-fetch Response as parameters.

profile.log((log) => {
  console.log("I was ran on ", log.date);
  console.log("I tried to get", log.query);
  console.log("I got", log.result);
});

Paginators

You can fetch until there are no more pages

const { tradeprices } = await profile.request()
  .tradeprices({}, (t) => t.fields('food','date'))
  .send();
await tradeprices.fetchAll();

Fetch until a callback returns false

const thisYear = '2022-01-01';
await tradeprices.fetchWhile((p) => p.lastItem?.date >= thisYear);

And then filter the results

const filtered = tradeprices.filter((t) => t.date >= thisYear);

Or fetch only the next page

await tradeprices.fetchMore();

The return type of these functions is a parsed version of paginator info

const p = await tradeprices.fetchMore();
console.log('The first price of food was', p.lastItem?.food);

That is also available in the first page

const p = tradeprices.info;
console.log('The last price of food was', p?.firstItem?.food);

You can also send executor options to these functions

await tradeprices.fetchAll({defer:false});
await tradeprices.fetchWhile(() => false, {defer:false});
await tradeprices.fetchMore({defer:false});

And then you use your result as you wish

tradeprices.forEach((d) => console.log('Food was',d.food,'on',d.date));

Writing an Executor

To create an executor you must import a few types

import { Executor, RequesterProfile, BaseRequest, Types } from 'pnw-rexec';
type Query = Types.Query;

Then you declare your config type

interface MyOptions {...}

Then create an executor class

class MyExecutor<O> implements Executor<MyOptions & O> {
  config: RequesterProfile<MyOptions & O>;
  executor: Executor<O>;
  defaultOptions: MyOptions & O;
}

config is the profile that created your executor

executor is the next executor down the line

defaultOptions is an object that merges executor previous

Now we need a constructor

constructor(
  config: RequesterProfile<O & MyOptions>,
  executor: Executor<O>,
  options: MyOptions & O
) {
  this.config = config;
  this.executor = executor;
  this.defaultOptions = options;
}

And a push() method, where you do your stuff and send the request.

async push<R>(
 requests: [keyof Query, BaseRequest<any, any>][],
 options?: O & MyOptions
): Promise<R> {
 // DoStuff
 // ...
 // Send
 const res = await this.executor.push(requests, options);
 return res as R;
}

To use your executor, you attach it to a profile like so

const profile = new RequesterProfile().executor(MyExecutor, {});

And that's it

Credits

This package was created by the tech team at Rose.

The basic types were generated with help from GraphQL Code Generator.

About

A Request Builder and Executor for the Politics and War API

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published