UNPKG

splitwise-ts

Version:

A typed, fast, flexible SDK for Splitwise written in TypeScript

313 lines (312 loc) 10.7 kB
import * as __WEBPACK_EXTERNAL_MODULE_es_toolkit_82663681__ from "es-toolkit"; import * as __WEBPACK_EXTERNAL_MODULE_ofetch__ from "ofetch"; import * as __WEBPACK_EXTERNAL_MODULE_oauth4webapi__ from "oauth4webapi"; class SplitwiseError extends Error { static __splitwise_error__ = true; code; cause; message; constructor({ message, code, cause }){ super(message); this.name = 'SplitwiseTSError'; this.message = message || ''; this.code = code || 500; this.cause = cause || 'splitwise'; } } function isError(input) { return input?.constructor?.__splitwise_error__ === true; } function createError(input) { if ('string' == typeof input) return new SplitwiseError({ message: input }); if (isError(input)) return input; const statusMessage = input.message ?? input.statusMessage ?? input.statusText; const err = new SplitwiseError({ message: statusMessage ?? '', cause: input.cause || 'splitwise', code: input.statusCode ?? input?.code }); return err; } const flatten = (payload, prefix = '', delimitter = '_')=>{ const result = {}; const keys = Object.keys(payload); for(let idx = 0; idx < keys.length; idx++){ const key = keys[idx]; const value = payload[key]; const prefixedKey = prefix ? `${prefix}${delimitter}${key}` : key; if ((0, __WEBPACK_EXTERNAL_MODULE_es_toolkit_82663681__.isPlainObject)(value) && Object.keys(value).length > 0) { Object.assign(result, flatten(value, prefixedKey)); continue; } if (Array.isArray(value)) { Object.assign(result, flatten(value, prefixedKey)); continue; } result[prefixedKey] = value; } return result; }; const splitwisify = (input)=>{ const flattened = flatten(input); const mapped = (0, __WEBPACK_EXTERNAL_MODULE_es_toolkit_82663681__.mapValues)(flattened, (value)=>(0, __WEBPACK_EXTERNAL_MODULE_es_toolkit_82663681__.isPrimitive)(value) ? value : ''); return mapped; }; const makeResponse = async (promise)=>{ try { return await promise; } catch (error) { throw createError(error); } }; const getFetcher = ()=>__WEBPACK_EXTERNAL_MODULE_ofetch__.ofetch.create({ retry: 3, retryDelay: 500 }); async function rest({ fetcher, auth, endpoint, params, requestBody, method, baseUrl }) { const accessToken = auth?.accessToken; if (!accessToken) throw createError({ cause: 'auth', code: 402, message: 'Access token is missing' }); const includeBody = [ 'post', 'put' ].includes(method.toLowerCase()) && !!requestBody; const promise = fetcher(endpoint, { baseURL: baseUrl, method, headers: { authorization: `Bearer ${accessToken}` }, query: params, body: includeBody ? requestBody : void 0 }); return makeResponse(promise); } const config = { issuer: 'https://secure.splitwise.com', authorization_endpoint: 'https://secure.splitwise.com/oauth/authorize', token_endpoint: 'https://secure.splitwise.com/oauth/token', api_url: 'https://secure.splitwise.com/api/v3.0' }; class Client { #auth; #defaultRequestOptions; constructor(auth){ this.#auth = auth; this.#defaultRequestOptions = { auth: this.#auth, fetcher: getFetcher(), endpoint: '', method: 'GET', baseUrl: config.api_url }; } users = { getCurrentUser: ()=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_current_user", method: 'get' }), getUser: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/get_user/${id}`, method: 'get' }), updateUser: (id, request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: `/update_user/${id}`, requestBody: request_body, method: 'post' }) }; groups = { getGroups: ()=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_groups", method: 'get' }), getGroup: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/get_group/${id}`, method: 'get' }), createGroup: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/create_group", requestBody: request_body, method: 'post' }), deleteGroup: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/delete_group/${id}`, method: 'post' }), unDeleteGroup: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/undelete_group/${id}`, method: 'post' }), addUserToGroup: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/add_user_to_group", requestBody: request_body, method: 'post' }), removeUserFromGroup: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/remove_user_from_group", requestBody: request_body, method: 'post' }) }; friends = { getFriends: ()=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_friends", method: 'get' }), getFriend: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/get_friend/${id}`, method: 'get' }), createFriend: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/create_friend", requestBody: request_body, method: 'post' }), createFriends: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/create_friends", requestBody: request_body, method: 'post' }), deleteFriend: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/delete_friend/${id}`, method: 'post' }) }; expenses = { getExpense: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/get_expense/${id}`, method: 'get' }), getExpenses: (params)=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_expenses", params: params, method: 'get' }), createExpense: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/create_expense", requestBody: request_body, method: 'post' }), updateExpense: (id, request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: `/update_expense/${id}`, requestBody: request_body, method: 'post' }), deleteExpense: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/delete_expense/${id}`, method: 'post' }), unDeleteExpense: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/undelete_expense/${id}`, method: 'post' }) }; comments = { getComments: (params)=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_comments", params: params, method: 'get' }), createComment: (request_body)=>rest({ ...this.#defaultRequestOptions, endpoint: "/create_comment", requestBody: request_body, method: 'post' }), deleteComment: (id)=>rest({ ...this.#defaultRequestOptions, endpoint: `/delete_comment/${id}`, method: 'post' }) }; notifications = { getNotifications: (params)=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_notifications", params: params, method: 'get' }) }; other = { getCurrencies: ()=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_currencies", method: 'get' }), getCategories: ()=>rest({ ...this.#defaultRequestOptions, endpoint: "/get_categories", method: 'get' }) }; } class OAuth2User { token; #options; constructor(credentials){ this.#options = credentials; } get accessToken() { return this.token ?? null; } async requestAccessToken() { if (!this.#options?.clientId || !this.#options?.clientSecret) throw createError({ cause: 'auth', message: 'Both clientId and clientSecret is required', code: 401 }); const { clientId, clientSecret } = this.#options; const client = { client_id: clientId }; const as = config; const params = (0, __WEBPACK_EXTERNAL_MODULE_oauth4webapi__.validateAuthResponse)(as, client, new URLSearchParams()); const clientAuth = (0, __WEBPACK_EXTERNAL_MODULE_oauth4webapi__.ClientSecretPost)(clientSecret); try { const response = await (0, __WEBPACK_EXTERNAL_MODULE_oauth4webapi__.clientCredentialsGrantRequest)(as, client, clientAuth, params); const token = await (0, __WEBPACK_EXTERNAL_MODULE_oauth4webapi__.processClientCredentialsResponse)(as, client, response); this.token = token.access_token; return { access_token: this.token }; } catch (e) { const responseError = e; const cause = responseError?.error; const code = responseError?.status; throw createError({ cause: 'auth', code, message: cause }); } } } export { Client, OAuth2User, splitwisify };