Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const IntegrationList: React.FC<Props> = function ({ instances }) {
</Card>
{instances.map((instance) => {
return (
<Card className="mb-10">
<Card className="mb-10" key={instance.id}>
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes a React warning that snuck in.

<CardContent>
<div className="flex justify-between">
<div>
Expand Down
10 changes: 5 additions & 5 deletions core/lib/contracts/resources/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type SuggestedMember = z.infer<typeof SuggestedMembersSchema>;
export const integrationsApi = contract.router({
createPub: {
method: "POST",
path: "integrations/:instanceId/pubs",
path: "/integrations/:instanceId/pubs",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@qweliant I wasn't able to get these routes working without the leading /.

summary: "Creates a new pub",
description: "A way to create a new pub",
body: PubPostSchema,
Expand All @@ -35,7 +35,7 @@ export const integrationsApi = contract.router({
},
getPub: {
method: "GET",
path: "integrations/:instanceId/pubs/:pubId",
path: "/integrations/:instanceId/pubs/:pubId",
summary: "Gets a pub",
description: "A way to get pubs fields for an integration instance",
pathParams: z.object({
Expand All @@ -48,7 +48,7 @@ export const integrationsApi = contract.router({
},
getAllPubs: {
method: "GET",
path: "integrations/:instanceId/pubs",
path: "/integrations/:instanceId/pubs",
summary: "Gets all pubs for this instance",
description: "A way to get all pubs for an integration instance",
pathParams: z.object({
Expand All @@ -60,7 +60,7 @@ export const integrationsApi = contract.router({
},
updatePub: {
method: "PATCH",
path: "integrations/:instanceId/pubs/:pubId",
path: "/integrations/:instanceId/pubs/:pubId",
summary: "Adds field(s) to a pub",
description: "A way to update a field for an existing pub",
body: PubFieldsSchema,
Expand All @@ -74,7 +74,7 @@ export const integrationsApi = contract.router({
},
getSuggestedMembers: {
method: "GET",
path: "integrations/:instanceId/autosuggest/members/:memberCandidateString",
path: "/integrations/:instanceId/autosuggest/members/:memberCandidateString",
summary: "autosuggest member",
description:
"A way to autosuggest members so that integrations users can find users or verify they exist. Will return a name for ",
Expand Down
2 changes: 1 addition & 1 deletion integrations/evaluations/app/configure/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default async function Page(props: Props) {
const instance = (await findInstance(props.searchParams.instanceId)) ?? makeInstance();
return (
<>
<h1>Configure</h1>
<h2>Configure</h2>
<pre>
<code>{JSON.stringify(instance)}</code>
</pre>
Expand Down
5 changes: 4 additions & 1 deletion integrations/evaluations/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export const metadata = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
<body>
<h1>Evalutions</h1>
{children}
</body>
</html>
);
}
17 changes: 14 additions & 3 deletions integrations/evaluations/app/run/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { makeClient } from "@pubpub/sdk";
import { findInstance, makeInstance } from "~/lib/instance";
import manifest from "~/pubpub-integration.json";

type Props = { searchParams: { instanceId: string } };
type Props = { searchParams: { instanceId: string; pubId: string } };

const client = makeClient(manifest);

export default async function Page(props: Props) {
const instance = (await findInstance(props.searchParams.instanceId)) ?? makeInstance();
const { instanceId, pubId } = props.searchParams;
const instance = (await findInstance(instanceId)) ?? makeInstance();
const { title } = await client.get(instanceId, pubId, "title");
return (
<>
<h1>Run</h1>
<h2>Run</h2>
<p>Instance</p>
<pre>
<code>{JSON.stringify(instance)}</code>
Copy link
Copy Markdown
Collaborator Author

@3mcd 3mcd Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the client types all pub values as unknown so I just do an explicit conversion/cast here so TS doesn't yell at me.

</pre>
<p>Pub Values</p>
<pre>
<code>{String(title)}</code>
</pre>
</>
);
}
6 changes: 5 additions & 1 deletion integrations/evaluations/pubpub-integration.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"name": "evaluations",
"read": {},
"read": {
"title": {
"id": "aa63cab5-3729-4a12-813b-eae316eceb69"
}
},
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't do much, just a placeholder manifest.

"write": {}
}
2 changes: 1 addition & 1 deletion packages/sdk/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# @pubpub/integration-sdk
# @pubpub/sdk
32 changes: 21 additions & 11 deletions packages/sdk/src/lib/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PubPubError, ResponseError } from "./errors";
import { InvalidFieldError, PubPubError, ResponseError } from "./errors";
import { Manifest } from "./types";

export type Get<T extends Manifest> = (
Expand All @@ -17,29 +17,35 @@ export type Put<T extends Manifest> = Record<
unknown
>;

export type PutResponse<T extends string[]> = {
export type PatchResponse<T extends string[]> = {
[K in T[number]]: unknown;
};

export type Client<T extends Manifest> = {
get<U extends Get<T>>(instanceId: string, pubId: string, ...fields: U): Promise<GetResponse<U>>;
put<U extends string[]>(
patch<U extends string[]>(
instanceId: string,
pubId: string,
patch: Put<T>
): Promise<PutResponse<U>>;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed put to patch since the verb changed in core.

): Promise<PatchResponse<U>>;
};

export const makeClient = <T extends Manifest>(manifest: T): Client<T> => {
const write = new Set(manifest.write ? Object.keys(manifest.write) : null);
const read = new Set(manifest.read ? [write.values(), ...Object.keys(manifest.read)] : write);
return {
async get(instanceId, pubId, ...fields) {
const signal = AbortSignal.timeout(5000);
try {
for (let i = 0; i < fields.length; i++) {
if (!read.has(fields[i])) {
throw new InvalidFieldError(`Field ${fields[i]} is not readable`);
}
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snuck this in as a contrived example of the SDK doing runtime validation of pub fields.

const response = await fetch(
`${process.env.PUBPUB_URL}/api/${instanceId}/pubs/${pubId}`,
`${process.env.PUBPUB_URL}/api/v0/integrations/${instanceId}/pubs/${pubId}`,
{
method: "GET",
signal,
signal: AbortSignal.timeout(5000),
headers: { "Content-Type": "application/json" },
}
);
Expand All @@ -57,14 +63,18 @@ export const makeClient = <T extends Manifest>(manifest: T): Client<T> => {
throw new PubPubError("Failed to get Pub", { cause });
}
},
async put(instanceId, pubId, patch) {
async patch(instanceId, pubId, patch) {
try {
const signal = AbortSignal.timeout(5000);
for (const key in patch) {
if (!write.has(key)) {
throw new InvalidFieldError(`Field ${key} is not writeable`);
}
}
const response = await fetch(
`${process.env.PUBPUB_URL}/api/${instanceId}/pubs/${pubId}`,
`${process.env.PUBPUB_URL}/api/v0/instances/${instanceId}/pubs/${pubId}`,
{
method: "PUT",
signal,
signal: AbortSignal.timeout(5000),
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fields: patch }),
}
Expand Down
2 changes: 2 additions & 0 deletions packages/sdk/src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ export class ResponseError extends IntegrationError {
}

export class PubPubError extends IntegrationError {}

export class InvalidFieldError extends IntegrationError {}