Skip to content

Commit

Permalink
added client functions
Browse files Browse the repository at this point in the history
  • Loading branch information
fomalhautb committed Oct 4, 2024
1 parent 898188e commit eef502f
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 0 deletions.
64 changes: 64 additions & 0 deletions packages/stack-shared/src/interface/clientInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ReadonlyJson } from '../utils/json';
import { filterUndefined } from '../utils/objects';
import { Result } from "../utils/results";
import { deindent } from '../utils/strings';
import { ContactChannelsCrud } from './crud/contact-channels';
import { CurrentUserCrud } from './crud/current-user';
import { ConnectedAccountAccessTokenCrud } from './crud/oauth';
import { InternalProjectsCrud, ProjectsCrud } from './crud/projects';
Expand Down Expand Up @@ -1099,5 +1100,68 @@ export class StackClientInterface {
session,
);
}

async createClientContactChannel(
data: ContactChannelsCrud['Client']['Create'],
session: InternalSession,
): Promise<ContactChannelsCrud['Client']['Read']> {
const response = await this.sendClientRequest(
"/contact-channels",
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
session,
);
return await response.json();
}

async updateClientContactChannel(
id: string,
data: ContactChannelsCrud['Client']['Update'],
session: InternalSession,
): Promise<ContactChannelsCrud['Client']['Read']> {
const response = await this.sendClientRequest(
`/contact-channels/me/${id}`,
{
method: "PATCH",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
session,
);
return await response.json();
}

async deleteClientContactChannel(
id: string,
session: InternalSession,
): Promise<void> {
await this.sendClientRequest(
`/contact-channels/me/${id}`,
{
method: "DELETE",
},
session,
);
}

async listClientContactChannels(
session: InternalSession,
): Promise<ContactChannelsCrud['Client']['Read'][]> {
const response = await this.sendClientRequest(
"/contact-channels?user_id=me",
{
method: "GET",
},
session,
);
return await response.json();
}
}

63 changes: 63 additions & 0 deletions packages/stack-shared/src/interface/serverInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ClientInterfaceOptions,
StackClientInterface
} from "./clientInterface";
import { ContactChannelsCrud } from "./crud/contact-channels";
import { CurrentUserCrud } from "./crud/current-user";
import { ConnectedAccountAccessTokenCrud } from "./crud/oauth";
import { TeamMemberProfilesCrud } from "./crud/team-member-profiles";
Expand Down Expand Up @@ -384,4 +385,66 @@ export class StackServerInterface extends StackClientInterface {
null,
);
}

async createServerContactChannel(
data: ContactChannelsCrud['Server']['Create'],
): Promise<ContactChannelsCrud['Server']['Read']> {
const response = await this.sendServerRequest(
"/contact-channels",
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
null,
);
return await response.json();
}

async updateServerContactChannel(
userId: string,
contactChannelId: string,
data: ContactChannelsCrud['Server']['Update'],
): Promise<ContactChannelsCrud['Server']['Read']> {
const response = await this.sendServerRequest(
`/contact-channels/${userId}/${contactChannelId}`,
{
method: "PATCH",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(data),
},
null,
);
return await response.json();
}

async deleteServerContactChannel(
userId: string,
contactChannelId: string,
): Promise<void> {
await this.sendServerRequest(
`/contact-channels/${userId}/${contactChannelId}`,
{
method: "DELETE",
},
null,
);
}

async listServerContactChannels(
userId: string,
): Promise<ContactChannelsCrud['Server']['Read'][]> {
const response = await this.sendServerRequest(
`/contact-channels?user_id=${userId}`,
{
method: "GET",
},
null,
);
return await response.json();
}
}
141 changes: 141 additions & 0 deletions packages/stack/src/lib/stack-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { KnownErrors, StackAdminInterface, StackClientInterface, StackServerInte
import { ProductionModeError, getProductionModeErrors } from "@stackframe/stack-shared/dist/helpers/production-mode";
import { ApiKeyCreateCrudRequest, ApiKeyCreateCrudResponse } from "@stackframe/stack-shared/dist/interface/adminInterface";
import { ApiKeysCrud } from "@stackframe/stack-shared/dist/interface/crud/api-keys";
import { ContactChannelsCrud } from "@stackframe/stack-shared/dist/interface/crud/contact-channels";
import { CurrentUserCrud } from "@stackframe/stack-shared/dist/interface/crud/current-user";
import { EmailTemplateCrud, EmailTemplateType } from "@stackframe/stack-shared/dist/interface/crud/email-templates";
import { InternalProjectsCrud, ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
Expand Down Expand Up @@ -331,6 +332,11 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
return await this._interface.getTeamMemberProfile({ teamId, userId: 'me' }, session);
}
);
private readonly _clientContactChannelsCache = createCacheBySession<[], ContactChannelsCrud['Client']['Read'][]>(
async (session) => {
return this._interface.listClientContactChannels(session);

Check failure on line 337 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / lint_and_build (22.x)

Returning an awaited promise is required in this context

Check failure on line 337 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

Returning an awaited promise is required in this context

Check failure on line 337 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

Returning an awaited promise is required in this context
}
);

protected async _getUserOAuthConnectionCacheFn(options: {
getUser: () => Promise<CurrentUserCrud['Client']['Read'] | null>,
Expand Down Expand Up @@ -750,6 +756,23 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
};
}

protected _clientContactChannelFromCrud(crud: ContactChannelsCrud['Client']['Read']): ContactChannel {
const app = this;
return {
id: crud.id,
value: crud.value,
type: crud.type,
isVerified: crud.is_verified,
usedForAuth: crud.used_for_auth,

async update(data: ContactChannelUpdateOptions) {
await app._interface.updateClientContactChannel(crud.id, contactChannelUpdateOptionsToCrud(data), app._getSession());
},
async delete() {
await app._interface.deleteClientContactChannel(crud.id, app._getSession());
},
};
}
protected _createAuth(session: InternalSession): Auth {
const app = this;
return {
Expand Down Expand Up @@ -924,6 +947,19 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
await app._interface.deleteCurrentUser(session);
session.markInvalid();
},
async listContactChannels() {
const result = await app._clientContactChannelsCache.getOrWait([session], "write-only");
return result.map((crud) => app._clientContactChannelFromCrud(crud));
},
useContactChannels() {
const result = useAsyncCache(app._clientContactChannelsCache, [session], "user.useContactChannels()");
return result.map((crud) => app._clientContactChannelFromCrud(crud));
},
async createContactChannel(data: ContactChannelCreateOptions) {
const crud = await app._interface.createClientContactChannel(contactChannelCreateOptionsToCrud('me', data), session);
await app._clientContactChannelsCache.refresh([session]);
return app._clientContactChannelFromCrud(crud);
},
};
}

Expand Down Expand Up @@ -1511,6 +1547,11 @@ class _StackServerAppImpl<HasTokenStore extends boolean, ProjectId extends strin
return await this._interface.getServerTeamMemberProfile({ teamId, userId });
}
);
private readonly _serverContactChannelsCache = createCache<[string], ContactChannelsCrud['Server']['Read'][]>(
async ([userId]) => {
return this._interface.listServerContactChannels(userId);

Check failure on line 1552 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / lint_and_build (22.x)

Returning an awaited promise is required in this context

Check failure on line 1552 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

Returning an awaited promise is required in this context

Check failure on line 1552 in packages/stack/src/lib/stack-app.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

Returning an awaited promise is required in this context
}
);

private async _updateServerUser(userId: string, update: ServerUserUpdateOptions): Promise<UsersCrud['Server']['Read']> {
const result = await this._interface.updateServerUser(userId, serverUserUpdateOptionsToCrud(update));
Expand All @@ -1530,6 +1571,19 @@ class _StackServerAppImpl<HasTokenStore extends boolean, ProjectId extends strin
};
}

protected _serverContactChannelFromCrud(userId: string, crud: ContactChannelsCrud['Server']['Read']): ServerContactChannel {
const app = this;
return {
...this._clientContactChannelFromCrud(crud),
async update(data: ServerContactChannelUpdateOptions) {
await app._interface.updateServerContactChannel(userId, crud.id, serverContactChannelUpdateOptionsToCrud(data));
},
async delete() {
await app._interface.deleteServerContactChannel(userId, crud.id);
},
};
}

constructor(options:
| StackServerAppConstructorOptions<HasTokenStore, ProjectId>
| {
Expand Down Expand Up @@ -1692,6 +1746,19 @@ class _StackServerAppImpl<HasTokenStore extends boolean, ProjectId extends strin
const result = useAsyncCache(app._serverUserTeamProfileCache, [team.id, crud.id], "user.useTeamProfile()");
return useMemo(() => app._serverEditableTeamProfileFromCrud(result), [result]);
},
async listContactChannels() {
const result = await app._serverContactChannelsCache.getOrWait([crud.id], "write-only");
return result.map((data) => app._serverContactChannelFromCrud(crud.id, data));
},
useContactChannels() {
const result = useAsyncCache(app._serverContactChannelsCache, [crud.id], "user.useContactChannels()");
return useMemo(() => result.map((data) => app._serverContactChannelFromCrud(crud.id, data)), [result]);
},
createContactChannel: async (data: ServerContactChannelCreateOptions) => {
const contactChannel = await app._interface.createServerContactChannel(serverContactChannelCreateOptionsToCrud(crud.id, data));
await app._serverContactChannelsCache.refresh([crud.id]);
return app._serverContactChannelFromCrud(crud.id, contactChannel);
},
};
}

Expand Down Expand Up @@ -2206,6 +2273,72 @@ class _StackAdminAppImpl<HasTokenStore extends boolean, ProjectId extends string
}
}

type _______________CONTACT_CHANNEL_______________ = never; // this is a marker for VSCode's outline view

type ContactChannel = {
id: string,
value: string,
type: 'email',
isVerified: boolean,
usedForAuth: boolean,

update(data: ContactChannelUpdateOptions): Promise<void>,
delete(): Promise<void>,
}

type ContactChannelCreateOptions = {
value: string,
type: 'email',
usedForAuth: boolean,
}

function contactChannelCreateOptionsToCrud(userId: string, options: ContactChannelCreateOptions): ContactChannelsCrud["Client"]["Create"] {
return {
value: options.value,
type: options.type,
used_for_auth: options.usedForAuth,
user_id: userId,
};
}

type ContactChannelUpdateOptions = {
usedForAuth?: boolean,
value?: string,
}

function contactChannelUpdateOptionsToCrud(options: ContactChannelUpdateOptions): ContactChannelsCrud["Client"]["Update"] {
return {
value: options.value,
used_for_auth: options.usedForAuth,
};
}

type ServerContactChannel = ContactChannel;
type ServerContactChannelUpdateOptions = ContactChannelUpdateOptions & {
isVerified?: boolean,
}

function serverContactChannelUpdateOptionsToCrud(options: ServerContactChannelUpdateOptions): ContactChannelsCrud["Server"]["Update"] {
return {
value: options.value,
is_verified: options.isVerified,
used_for_auth: options.usedForAuth,
};
}

type ServerContactChannelCreateOptions = ContactChannelCreateOptions & {
isVerified?: boolean,
}
function serverContactChannelCreateOptionsToCrud(userId: string, options: ServerContactChannelCreateOptions): ContactChannelsCrud["Server"]["Create"] {
return {
type: options.type,
value: options.value,
is_verified: options.isVerified,
user_id: userId,
used_for_auth: options.usedForAuth,
};
}

type _______________USER_______________ = never; // this is a marker for VSCode's outline view
type ___________client_user = never; // this is a marker for VSCode's outline view

Expand Down Expand Up @@ -2366,6 +2499,10 @@ type UserExtra = {
*/
update(update: UserUpdateOptions): Promise<void>,

useContactChannels(): ContactChannel[],
listContactChannels(): Promise<ContactChannel[]>,
createContactChannel(data: ContactChannelCreateOptions): Promise<ContactChannel>,

delete(): Promise<void>,

getConnectedAccount(id: ProviderType, options: { or: 'redirect', scopes?: string[] }): Promise<OAuthConnection>,
Expand Down Expand Up @@ -2427,6 +2564,10 @@ type ServerBaseUser = {
setServerMetadata(metadata: any): Promise<void>,
setClientReadOnlyMetadata(metadata: any): Promise<void>,

useContactChannels(): ServerContactChannel[],
listContactChannels(): Promise<ServerContactChannel[]>,
createContactChannel(data: ServerContactChannelCreateOptions): Promise<ServerContactChannel>,

updatePassword(options: { oldPassword?: string, newPassword: string}): Promise<KnownErrors["PasswordConfirmationMismatch"] | KnownErrors["PasswordRequirementsNotMet"] | void>,

update(user: ServerUserUpdateOptions): Promise<void>,
Expand Down

0 comments on commit eef502f

Please sign in to comment.