Skip to content

Commit

Permalink
feat: add better query builder (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
ghostrider-05 authored Jan 3, 2025
1 parent eee1902 commit b64a95e
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 132 deletions.
99 changes: 9 additions & 90 deletions src/rest/v2/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RequestPayload } from '../../payloads/v2/internals/request'

import type { Type, RelationshipFields, RelationshipMap } from '../../schemas/v2'
import { type Type, type RelationshipFields, type RelationshipMap, QueryBuilder } from '../../schemas/v2'

export type BasePatreonQuery = {
/**
Expand All @@ -16,23 +16,6 @@ export type BasePatreonQuery = {
params: URLSearchParams
}

type ValueOrArray<T> = T | T[]

type PaginationQuerySort =
| string
| { key: string, descending?: boolean }

export type PaginationQuery = {
cursor?: string
count?: number
sort?: ValueOrArray<PaginationQuerySort>
}

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface QueryRequestOptions extends PaginationQuery {

}

export type PatreonQuery<
T extends Type,
Includes extends RelationshipFields<T>,
Expand Down Expand Up @@ -63,36 +46,6 @@ export type GetResponsePayload<Query extends BasePatreonQuery> = Query extends P
? PayloadFromQuery<T, I, A, L, Query>
: never

/**
* Helper function to convert pagination sort options to a parameter
* @param options the sort options
* @returns the parameter to include in the query
*/
function resolveSortOptions(options: ValueOrArray<PaginationQuerySort>): string {
return (Array.isArray(options) ? options : [options])
.map(option => {
return typeof option === 'string'
? option
: (option.descending ? `-${option.key}` : option.key)
})
.join(',')
}

/**
* Helper function to convert query options to parameter options
* @param options the request options
* @returns the parameters options
*/
function resolveQueryOptions(options?: QueryRequestOptions): Record<string, string> {
const params: Record<string, string> = {}

if (options?.count != undefined) params['page[count]'] = options.count.toString()
if (options?.cursor != undefined) params['page[cursor]'] = options.cursor
if (options?.sort != undefined) params['sort'] = resolveSortOptions(options.sort)

return params
}

/**
* Helper function to create a Patreon query from URLSearchParams
* @param params the parameters for the request.
Expand All @@ -116,47 +69,13 @@ export function createQuery<Q extends BasePatreonQueryType<Type, boolean>>(param
} as Q
}

/**
* Create a stronly typed query for the Patreon API
* @returns to add include, attributes and options to the query
*/
function _buildQuery<
T extends Extract<
Type,
| Type.Campaign
| Type.Member
| Type.Post
| Type.User
| Type.Webhook
>,
Listing extends boolean = false
>() {
return function <Includes extends RelationshipFields<`${T}`> = never>(include?: Includes[]) {
return function <
Attributes extends RelationshipMap<T, Includes>,
>(attributes?: Attributes, options?: QueryRequestOptions): PatreonQuery<T, Includes, Attributes, Listing> {
const attr: Partial<Attributes> = attributes ?? {}

const params = new URLSearchParams({
...(include ? { include: include.join(',') } : {}),
...Object
.keys(attr)
.reduce((params, key) => ({ ...params, [`fields[${key}]`]: attr[key].join(',') }), {}),
...resolveQueryOptions(options),
})

return createQuery(params)
}
}
}

export const buildQuery = {
identity: _buildQuery<Type.User, false>(),
campaign: _buildQuery<Type.Campaign, false>(),
campaigns: _buildQuery<Type.Campaign, true>(),
campaignMembers: _buildQuery<Type.Member, true>(),
member: _buildQuery<Type.Member, false>(),
campaignPosts: _buildQuery<Type.Post, true>(),
post: _buildQuery<Type.Post, false>(),
webhooks: _buildQuery<Type.Webhook, true>(),
identity: QueryBuilder.createFunctionBuilder(QueryBuilder.identity),
campaign: QueryBuilder.createFunctionBuilder(QueryBuilder.campaign),
campaigns: QueryBuilder.createFunctionBuilder(QueryBuilder.campaigns),
campaignMembers: QueryBuilder.createFunctionBuilder(QueryBuilder.campaignMembers),
member: QueryBuilder.createFunctionBuilder(QueryBuilder.member),
campaignPosts: QueryBuilder.createFunctionBuilder(QueryBuilder.campaignPosts),
post: QueryBuilder.createFunctionBuilder(QueryBuilder.post),
webhooks: QueryBuilder.createFunctionBuilder(QueryBuilder.webhooks),
}
78 changes: 39 additions & 39 deletions src/schemas/v2/generated/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const Address = {
resource: 'address',
properties: [
resource: <const>'address',
properties: <const>[
'addressee',
'city',
'country',
Expand All @@ -11,7 +11,7 @@ export const Address = {
'postal_code',
'state',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaigns', type: 'array'
},
Expand All @@ -21,8 +21,8 @@ export const Address = {
],
}
export const Benefit = {
resource: 'benefit',
properties: [
resource: <const>'benefit',
properties: <const>[
'app_external_id',
'app_meta',
'benefit_type',
Expand All @@ -39,7 +39,7 @@ export const Benefit = {
'tiers_count',
'title',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'array'
},
Expand All @@ -52,8 +52,8 @@ export const Benefit = {
],
}
export const Campaign = {
resource: 'campaign',
properties: [
resource: <const>'campaign',
properties: <const>[
'vanity',
'url',
'thanks_video_url',
Expand Down Expand Up @@ -82,7 +82,7 @@ export const Campaign = {
'creation_name',
'created_at',
],
relationships: [
relationships: <const>[
{
resource: 'benefit', name: 'benefits', type: 'array'
},
Expand All @@ -98,13 +98,13 @@ export const Campaign = {
],
}
export const Deliverable = {
resource: 'deliverable',
properties: [
resource: <const>'deliverable',
properties: <const>[
'completed_at',
'delivery_status',
'due_at',
],
relationships: [
relationships: <const>[
{
resource: 'benefit', name: 'benefit', type: 'item'
},
Expand All @@ -120,18 +120,18 @@ export const Deliverable = {
],
}
export const Goal = {
resource: 'goal',
properties: [
resource: <const>'goal',
properties: <const>[
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'item'
},
],
}
export const Media = {
resource: 'media',
properties: [
resource: <const>'media',
properties: <const>[
'created_at',
'download_url',
'file_name',
Expand All @@ -147,12 +147,12 @@ export const Media = {
'upload_parameters',
'upload_url',
],
relationships: [
relationships: <const>[
],
}
export const Member = {
resource: 'member',
properties: [
resource: <const>'member',
properties: <const>[
'campaign_lifetime_support_cents',
'currently_entitled_amount_cents',
'email',
Expand All @@ -170,7 +170,7 @@ export const Member = {
'pledge_relationship_start',
'will_pay_amount_cents',
],
relationships: [
relationships: <const>[
{
resource: 'address', name: 'address', type: 'item'
},
Expand All @@ -189,8 +189,8 @@ export const Member = {
],
}
export const OauthClient = {
resource: 'client',
properties: [
resource: <const>'client',
properties: <const>[
'author_name',
'category',
'client_secret',
Expand All @@ -204,7 +204,7 @@ export const OauthClient = {
'tos_url',
'version',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'item'
},
Expand All @@ -214,8 +214,8 @@ export const OauthClient = {
],
}
export const PledgeEvent = {
resource: 'pledge-event',
properties: [
resource: <const>'pledge-event',
properties: <const>[
'amount_cents',
'currency_code',
'date',
Expand All @@ -225,7 +225,7 @@ export const PledgeEvent = {
'tier_title',
'type',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'item'
},
Expand All @@ -238,8 +238,8 @@ export const PledgeEvent = {
],
}
export const Post = {
resource: 'post',
properties: [
resource: <const>'post',
properties: <const>[
'app_id',
'app_status',
'content',
Expand All @@ -252,7 +252,7 @@ export const Post = {
'title',
'url',
],
relationships: [
relationships: <const>[
{
resource: 'user', name: 'user', type: 'item'
},
Expand All @@ -262,8 +262,8 @@ export const Post = {
],
}
export const Tier = {
resource: 'tier',
properties: [
resource: <const>'tier',
properties: <const>[
'amount_cents',
'created_at',
'description',
Expand All @@ -281,7 +281,7 @@ export const Tier = {
'url',
'user_limit',
],
relationships: [
relationships: <const>[
{
resource: 'benefit', name: 'benefits', type: 'array'
},
Expand All @@ -294,8 +294,8 @@ export const Tier = {
],
}
export const User = {
resource: 'user',
properties: [
resource: <const>'user',
properties: <const>[
'about',
'can_see_nsfw',
'created',
Expand All @@ -313,7 +313,7 @@ export const User = {
'url',
'vanity',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'item'
},
Expand All @@ -323,16 +323,16 @@ export const User = {
],
}
export const Webhook = {
resource: 'webhook',
properties: [
resource: <const>'webhook',
properties: <const>[
'last_attempted_at',
'num_consecutive_times_failed',
'paused',
'secret',
'triggers',
'uri',
],
relationships: [
relationships: <const>[
{
resource: 'campaign', name: 'campaign', type: 'item'
},
Expand Down
1 change: 1 addition & 0 deletions src/schemas/v2/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './item'
export * from './query'
export * from './relationships'

export * from './resources/address'
Expand Down
Loading

0 comments on commit b64a95e

Please sign in to comment.