Git Product home page Git Product logo

splid-js's Introduction

Splid.Js

a reverse-engineered typescript client for the Splid (https://splid.app) API. at the moment, only read operations are supported.

Install

install the package:

npm install splid-js

Examples

// basic usage
import { SplidClient } from 'splid-js';

async function main() {
  const client = new SplidClient();

  const inviteCode = 'PWJ E2B P7A';

  const groupRes = await client.group.getByInviteCode(inviteCode);

  const groupInfoRes = await client.groupInfo.getByGroup(
    groupRes.result.objectId
  );

  const entriesRes = await client.entry.getByGroup(groupRes.result.objectId);

  const membersRes = await client.person.getByGroup(groupRes.result.objectId);

  const expensesAndPayments = await client.entry.getByGroup(
    groupRes.result.objectId
  );
}
main();
// using the returned data
import { SplidClient } from 'splid-js';

const formatCurrency = (amount: number, currencyCode: string) => {
  return (
    amount.toLocaleString(undefined, {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    }) + currencyCode
  );
};

const getEntryDescription = (
  entry: SplidJs.Entry,
  members: SplidJs.Person[]
) => {
  const primaryPayer = members.find((j) => j.GlobalId === entry.primaryPayer);

  for (const item of entry.items) {
    const totalAmount = item.AM;

    const profiteers = Object.entries(item.P.P).map(([userId, share]) => {
      const user = members.find((j) => j.GlobalId === userId);

      const shareText = formatCurrency(totalAmount * share, entry.currencyCode);

      return user.name + ' (' + shareText + ')';
    });
    const profiteersText = profiteers.join(', ');

    const totalText = formatCurrency(totalAmount, entry.currencyCode);

    if (entry.isPayment) {
      const description =
        primaryPayer.name + ' sent ' + totalText + ' to ' + profiteersText;

      return description;
    } else {
      const description =
        primaryPayer.name + ' payed ' + totalText + ' for ' + profiteersText;

      return description;
    }
  }
};

async function main() {
  const client = new SplidClient();

  const inviteCode = 'PWJ E2B P7A';

  const groupRes = await client.group.getByInviteCode(inviteCode);

  const groupInfoRes = await client.groupInfo.getByGroup(
    groupRes.result.objectId
  );

  const entriesRes = await client.entry.getByGroup(groupRes.result.objectId);

  const membersRes = await client.person.getByGroup(groupRes.result.objectId);

  for (const entry of entriesRes.result.results) {
    console.log(getEntryDescription(entry, membersRes.result.results));
  }
}
main();

splid-js's People

Contributors

nikeee avatar dependabot[bot] avatar linusbolls avatar

Stargazers

Ben Ralston avatar  avatar Steffen Holanger avatar

Watchers

 avatar Florian König avatar  avatar  avatar

splid-js's Issues

allow for batching of arbitrary requests

as pointed out by @nikeee, splid uses the parse framework, which supports batching arbitrary queries.
splid uses batching when creating multiple users at once by creating a group. testing has revealed that any splid requests can be batched together.
supporting this would allow us to do more by sending fewer requests to splid, and avoiding hitting their rate limit.
it would be cool to support this at some point in the future by using this api:

const [person1, person2] = await client.batch((b) => [
  b.person.create(groupId, {
    name: 'Linus Bolls',
    initials: 'LB',
  }),
  b.person.create(groupId, {
    name: 'Florian König',
    initials: 'FK',
  }),
]);

where b would be an instance of a new BatchClient class, which would have all methods of SplidClient.

Types referenced in package.json don't seem to work

When importing in a TS project:

Could not find a declaration file for module 'splid-js'.

$REDACTED/node_modules/splid-js/dist/index.modern.js' implicitly has an 'any' type.

There are types at $REDACTED/node_modules/splid-js/dist/src/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'splid-js' library may need to update its package.json or typings.

My tsconfig:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "NodeNext",
    "strict": true,
    "lib": [
      "ES2021"
    ],

    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,

    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "sourceMap": true
  },
  "include": [
    "src"
  ]
}

It works if I remove the exports field in the package.json inside the node_modules.

Consider using Parse directly

I'm not sure if this aligns with your goals (maybe you want it slim), but Splid uses the Parse Framework, which also has a JS API:

https://docs.parseplatform.org/js/guide/#getting-started

You can just use the keys from the client and perform operations. The Framework also handles Date (de)serialization. Maybe we can even do queries that the normal client does not do:

const Parse = require("parse/node");
Parse.initialize(
    "AKCaB0FCF0NIigWjxcDBpDYh7q6eN7gYfKxk5QBN",
    "4Z29DJvRGdVnB5dcTvDTTG01fbkITxvcPCPOt21M",
);
Parse.serverURL = "https://splid.herokuapp.com/parse"

(keys taken from your public repository)

Taken from my scratch pad:

function createGroup(): Promise<GroupInfo> {
    return Parse.Cloud.run("createGroup");
}

function fetchCodes(groupObjectId: string): Promise<Omit<GroupInfo, "objectId">> {
    return Parse.Cloud.run("fetchCodes", {
        group: groupObjectId
    });
}

type GroupInfo = {
    shortCode: string;
    longCode: string;
    extendedShortCode: string
    objectId: string;
}

type CurrencyCode = string;
type Person = {
    attributes: {
        name: string;
        initials: string;
        GlobalId: string;
        UpdateID: string;
        isDeleted: boolean,
        createdGlobally: Date;
        UpdateInstallationID: string; 
        createdAt: Date;
        updatedAt: Date;
        group: ParseUser;
    }
};

type ParseUser = {
    className: "_User";
    id: string;
}

type Entry = {
    attributes: {
        UpdateInstallationID: string;
        primaryPayer: string;
        UpdateID: string;
        title: string;
        GlobalId: string;
        isPayment: boolean,
        items: [
            {
                P: {
                    // This map is probably be used to model multiple people that purchased something
                    P: Record<string /* some uuid */, number>,
                }
                AM: number, // amount
            }
        ],
        currencyCode: CurrencyCode,
        createdGlobally: Date,
        group: ParseUser;
        createdAt: Date;
        updatedAt: Date;
    }
};

/**
 * @param code Despite the name, only `shortCode` is accepted.
 * @returns
 */
function joinGroupWithAnyCode(code: string): Promise<GroupInfo> {
    return Parse.Cloud.run("joinGroupWithAnyCode", { code });
}

function getCurrencyRates(): Promise<Record<CurrencyCode, number>> {
    return Parse.Cloud.run("getCurrencyRates");
}

function getCodeConfig(deviceType: "ios" | "android"): Promise<string> {
    return Parse.Cloud.run("getCodeConfig", { deviceType });
}

interface QueryParams {
    className: string;
    group: string;
    minDate?: Date;
    limit: number;
    skip: number;
}

function findObjects<T extends Parse.Attributes>(query: QueryParams) {
    return Parse.Cloud.run("findObjects", {
        ...query,
        minDate: query.minDate ?? new Date("1969-12-31T00:00:00.000Z"),
    }) as Promise<FindObjectsResponse<T>>;
}

type FindObjectsResponse<T extends Parse.Attributes> = {
    serverDate: Date;
    results: Parse.Object<T>[];
};

    const j = await joinGroupWithAnyCode("SHO RTC ODE")
    console.log(j);

    const o = await findObjects<Entry["attributes"]>({
        className: "Entry",
        group: j.objectId,
        minDate: undefined,
        limit: 10,
        skip: 0
    });

    console.log(o.results[0].attributes.items[0])

Caution: createGroup somehow creates a broken group with no name/currency/etc and may crash the client. There is something missing.

Concerning the SS field: It seems that has something to do with Sharing. If it is set, it has a float value and PT will be set to 1 (and 0 otherwise).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.