Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
david-plugge committed Jan 11, 2024
1 parent d035b43 commit 22cdab4
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 17 deletions.
18 changes: 1 addition & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PocketBase, { RecordModel, RecordService } from 'pocketbase';
import { GenericCollection, GenericSchema } from './types.js';
import { GenericSchema } from './types.js';
import { TypedRecordService } from './record-service.js';

export {
Expand Down Expand Up @@ -39,19 +39,3 @@ export interface TypedPocketBase<Schema extends GenericSchema>
): TypedRecordService<Schema[C]>;
collection<M = RecordModel>(idOrName: string): RecordService<M>;
}

class _TypedRecordService<Collection extends GenericCollection> {
constructor(readonly service: RecordService) {}

getOne(id: string) {
this.service.getOne(id, {});
}
}

class _TypedPocketBase<Schema extends GenericSchema> extends PocketBase {
from<CollectionName extends keyof Schema>(
name: CollectionName
): _TypedRecordService<Schema[CollectionName]> {
return new _TypedRecordService(this.collection(name as string));
}
}
273 changes: 273 additions & 0 deletions src/new/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
import PocketBase, {
ListResult,
OAuth2AuthConfig,
RecordAuthResponse,
RecordOptions,
RecordService,
SendOptions
} from 'pocketbase';
import {
GenericCollection,
GenericSchema,
MaybeArray,
RecordWithExpandToDotPath
} from '../types.js';
import {
ResolveSelectWithExpand,
SelectWithExpand,
resolveSelect
} from '../queryParams.js';
import { Sort } from '../sort.js';
import { Filter, FilterParam } from '../filter.js';

export interface ViewCollectionService<Collection extends GenericCollection> {
collectionName: Collection['collectionName'];
client: PocketBase;

getFullList<TSelect extends SelectWithExpand<Collection>>(
options?: {
select?: TSelect;
page?: number;
perPage?: number;
sort?: MaybeArray<Sort<Collection>>;
filter?: FilterParam<RecordWithExpandToDotPath<Collection>>;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>[]>;
getList<TSelect extends SelectWithExpand<Collection>>(
page?: number,
perPage?: number,
options?: {
select?: TSelect;
sort?: MaybeArray<Sort<Collection>>;
filter?: FilterParam<RecordWithExpandToDotPath<Collection>>;
} & SendOptions
): Promise<ListResult<ResolveSelectWithExpand<Collection, TSelect>>>;
getFirstListItem<TSelect extends SelectWithExpand<Collection>>(
filter: FilterParam<RecordWithExpandToDotPath<Collection>>,
options?: {
select?: TSelect;
sort?: MaybeArray<Sort<Collection>>;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
getOne<TSelect extends SelectWithExpand<Collection>>(
id: string,
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;

createFilter(filter: Filter<Collection>): Filter<Collection>;

createSort(...sort: Sort<Collection>[]): Sort<Collection>[];

createSelect<T extends SelectWithExpand<Collection>>(select: T): T;
}

export interface BaseCollectionService<Collection extends GenericCollection>
extends ViewCollectionService<Collection> {
create<TSelect extends SelectWithExpand<Collection>>(
bodyParams: Collection['create'],
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
update<TSelect extends SelectWithExpand<Collection>>(
id: string,
bodyParams: Collection['update'],
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
delete(id: string): Promise<boolean>;
}

export interface AuthCollectionService<Collection extends GenericCollection>
extends ViewCollectionService<Collection>,
Pick<RecordService, (typeof FORWARD_METHODS)[number]> {
authWithPassword<TSelect extends SelectWithExpand<Collection>>(
usernameOrEmail: string,
password: string,
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
authWithOAuth2Code<TSelect extends SelectWithExpand<Collection>>(
provider: string,
code: string,
codeVerifier: string,
redirectUrl: string,
createData?: {
[key: string]: any;
},
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
authWithOAuth2(
options: Omit<OAuth2AuthConfig, 'createData'> & {
createData?: Collection['create'];
} & SendOptions
): Promise<RecordAuthResponse<Collection['response']>>;
authRefresh<TSelect extends SelectWithExpand<Collection>>(
options?: {
select?: TSelect;
} & SendOptions
): Promise<ResolveSelectWithExpand<Collection, TSelect>>;
}

const FORWARD_METHODS = [
'listAuthMethods',
'requestPasswordReset',
'confirmPasswordReset',
'requestVerification',
'confirmVerification',
'requestEmailChange',
'confirmEmailChange',
'listExternalAuths',
'unlinkExternalAuth'
] as const;

export class TypedRecordService {
constructor(readonly service: RecordService) {
for (const name of FORWARD_METHODS) {
// @ts-ignore
this[name] = this.service[name].bind(this.service);
}
}

get client() {
return this.service.client;
}

get collectionName() {
return this.service.collectionIdOrName;
}

private prepareOptions(options?: RecordOptions): RecordOptions {
const opts = {
...options
};

const { expand, fields } = resolveSelect(options?.select);
const sort = Array.isArray(options?.sort)
? options.sort.join(',')
: options?.sort;

if (fields) opts.fields = fields;
if (expand) opts.expand = expand;
if (sort) opts.sort = sort;

return opts;
}

filter(filter: string) {
return filter;
}
sort(sort: string | string[]) {
return sort;
}
select(select: any) {
return select;
}

getFullList(options: {
select?: any;
page?: number;
perPage?: number;
sort?: string | string[];
filter?: string;
}) {
return this.service.getFullList(this.prepareOptions(options));
}

getList(
page?: number,
perPage?: number,
options?: {
select?: any;
sort?: string | string[];
filter?: string;
}
) {
return this.service.getList(
page,
perPage,
this.prepareOptions(options)
);
}

getFirstListItem(id: string) {
return this.service.getFirstListItem(id);
}

getOne(id: string): Promise<any> {
return this.service.getOne(id);
}

create() {
this.service.create({});
}

update(id: string) {
this.service.update(id, {});
}

delete(id: string) {
this.service.delete(id);
}

authWithPassword(
usernameOrEmail: string,
password: string,
options?: RecordOptions | undefined
): Promise<RecordAuthResponse> {
return this.service.authWithPassword(
usernameOrEmail,
password,
options
);
}

authWithOAuth2Code(
provider: string,
code: string,
codeVerifier: string,
redirectUrl: string,
createData?: { [key: string]: any } | undefined,
options?: RecordOptions | undefined
): Promise<RecordAuthResponse> {
return this.service.authWithOAuth2Code(
provider,
code,
codeVerifier,
redirectUrl,
createData,
options
);
}

authWithOAuth2(options: OAuth2AuthConfig): Promise<RecordAuthResponse> {
return this.service.authWithOAuth2(options);
}

authRefresh(
options?: RecordOptions | undefined
): Promise<RecordAuthResponse> {
return this.authRefresh(options);
}
}

export class TypedPocketBase<Schema extends GenericSchema> extends PocketBase {
from<
CollectionName extends keyof Schema,
Collection extends GenericCollection = Schema[CollectionName]
>(
name: CollectionName
): Collection['type'] extends 'view'
? ViewCollectionService<Collection>
: Collection['type'] extends 'base'
? BaseCollectionService<Collection>
: AuthCollectionService<Collection> {
return new TypedRecordService(this.collection(name as string)) as any;
}
}
49 changes: 49 additions & 0 deletions src/queryParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,55 @@ export interface CreateOptions {
): TypedRecordQueryParams<TSelect>;
}

export function resolveSelect(select: any) {
const fields: string[] = [];
const expand: string[] = [];

if (select) {
(function recurse(
{ $expand, ...rest }: SelectWithExpand<any>,
fieldsParent: string[] = [],
expandParent: string[] = []
) {
if (Object.keys(rest).length === 0) {
fields.push([...fieldsParent, '*'].join('.'));
} else {
for (const key in rest) {
if (rest[key]) {
fields.push([...fieldsParent, key].join('.'));
}
}
}

if ($expand) {
for (const key in $expand) {
const sub = $expand[key];
if (sub === true) {
expand.push([...expandParent, key].join('.'));
fields.push(
[...fieldsParent, 'expand', key, '*'].join('.')
);
} else if (sub) {
expand.push([...expandParent, key].join('.'));
recurse(
sub,
[...fieldsParent, 'expand', key],
[...expandParent, key]
);
}
}
}
})(select);
} else {
fields.push('*');
}

return {
fields: fields.join(','),
expand: expand.join(',')
};
}

export const createOptions: CreateOptions = (options) => {
const fields: string[] = [];
const expand: string[] = [];
Expand Down

0 comments on commit 22cdab4

Please sign in to comment.