Skip to content

Commit

Permalink
Memberships API, #61
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiancook committed Jul 28, 2023
1 parent 638c283 commit d565434
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/data/membership/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from "./get-membership";
export * from "./set-membership";
export * from "./store";
export * from "./delete-membership";
export * from "./list-memberships";
export * as membershipSchema from "./schema";
11 changes: 11 additions & 0 deletions src/data/membership/list-memberships.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Membership } from "./types";
import { getMembershipStore } from "./store";

export interface ListMembershipsInput {}

export async function listMemberships({}: ListMembershipsInput = {}): Promise<
Membership[]
> {
const store = getMembershipStore();
return store.values();
}
4 changes: 4 additions & 0 deletions src/data/membership/set-membership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,8 @@ export async function setMembership(data: PartialMembership) {
hash.update(data.reference);
return hash.digest().toString("hex");
}
}

export async function addMembership(data: PartialMembership) {
return setMembership(data);
}
41 changes: 41 additions & 0 deletions src/listen/membership/add-membership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {FastifyInstance} from "fastify";
import { addMembership, MembershipData, membershipSchema } from "../../data";
import { authenticate } from "../authentication";

export async function addMembershipRoutes(fastify: FastifyInstance) {
type Schema = {
Body: MembershipData;
};

const response = {
201: {
description: "A new membership",
...membershipSchema.membership,
},
};

const schema = {
description: "Add a new membership",
tags: ["membership"],
summary: "",
body: membershipSchema.membershipData,
response,
security: [
{
apiKey: [] as string[],
},
],
};

try {
fastify.post<Schema>("/", {
schema,
preHandler: authenticate(fastify),
async handler(request, response) {
const membership = await addMembership(request.body);
response.status(201);
response.send(membership);
},
});
} catch {}
}
54 changes: 54 additions & 0 deletions src/listen/membership/get-membership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {FastifyInstance} from "fastify";
import { getMembership, membershipSchema } from "../../data";
import { authenticate } from "../authentication";
import {isUnauthenticated} from "../../authentication";

export async function getMembershipRoutes(fastify: FastifyInstance) {
const params = {
type: "object",
properties: {
membershipId: {
type: "string",
},
},
required: ["membershipId"],
};

const response = {
200: {
description: "A membership",
...membershipSchema.membership,
},
};

const schema = {
description: "Get a membership",
tags: ["membership"],
summary: "",
response,
params,
security: [
{
apiKey: [] as string[],
},
],
};

type Schema = {
Params: {
membershipId: string;
};
};

try {
fastify.get<Schema>("/:membershipId", {
schema,
preHandler: authenticate(fastify),
async handler(request, response) {
const membership = await getMembership(request.params.membershipId);
if (!membership || (isUnauthenticated() && !membership.public)) response.status(404);
response.send(membership);
},
});
} catch {}
}
20 changes: 20 additions & 0 deletions src/listen/membership/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FastifyInstance } from "fastify";
import { listMembershipRoutes } from "./list-memberships";
import { addMembershipRoutes } from "./add-membership";
import { getMembershipRoutes } from "./get-membership";
import { setMembershipRoutes } from "./set-membership";
import { patchMembershipRoutes } from "./patch-memberships";

export async function membershipRoutes(fastify: FastifyInstance) {
async function routes(fastify: FastifyInstance) {
fastify.register(listMembershipRoutes);
fastify.register(addMembershipRoutes);
fastify.register(getMembershipRoutes);
fastify.register(setMembershipRoutes);
fastify.register(patchMembershipRoutes);
}

fastify.register(routes, {
prefix: "/memberships",
});
}
37 changes: 37 additions & 0 deletions src/listen/membership/list-memberships.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FastifyInstance, FastifyRequest } from "fastify";
import { listMemberships, membershipSchema } from "../../data";
import { authenticate } from "../authentication";
import {isUnauthenticated} from "../../authentication";

export async function listMembershipRoutes(fastify: FastifyInstance) {
const response = {
200: {
type: "array",
items: membershipSchema.membership,
},
};

const schema = {
description: "List of memberships",
tags: ["membership"],
summary: "",
response,
security: [
{
apiKey: [] as string[],
},
],
};

try {
fastify.get("/", {
schema,
preHandler: authenticate(fastify, { anonymous: true }),
async handler(request: FastifyRequest, response) {
response.send(await listMemberships({
public: isUnauthenticated()
}));
},
});
} catch { }
}
81 changes: 81 additions & 0 deletions src/listen/membership/patch-memberships.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {FastifyInstance} from "fastify";
import {getMembership, MembershipData, membershipSchema, setMembership} from "../../data";
import { authenticate } from "../authentication";

export async function patchMembershipRoutes(fastify: FastifyInstance) {
type Schema = {
Body: Partial<MembershipData>;
Params: {
membershipId: string;
}
};

const params = {
type: "object",
properties: {
membershipId: {
type: "string",
},
},
required: ["membershipId"],
};

const response = {
201: {
description: "Updated membership",
...membershipSchema.membership,
},
};

const schema = {
description: "Update an existing membership",
tags: ["membership"],
summary: "",
body: {
...membershipSchema.membershipData,
properties: Object.fromEntries(
Object.entries(membershipSchema.membershipData.properties)
.map(([key, value]) => {
if (typeof value !== "object" || Array.isArray(value)) return [key, value];
return [key, {
...value,
nullable: true
}]
})
),
required: [] as string[]
},
response,
params,
security: [
{
apiKey: [] as string[],
},
],
};

try {
fastify.patch<Schema>("/:membershipId", {
schema,
preHandler: authenticate(fastify),
async handler(request, response) {
const { membershipId } = request.params;
const existing = await getMembership(membershipId);
// Patch must have an existing membership
if (!existing) {
response.status(404);
return response.send();
}
const membership = await setMembership({
...existing,
...request.body,
createdAt: existing.createdAt,
membershipId
});
response.status(200);
response.send(membership);
},
});
} catch {
}
}
63 changes: 63 additions & 0 deletions src/listen/membership/set-membership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {FastifyInstance} from "fastify";
import {getMembership, MembershipData, membershipSchema, setMembership} from "../../data";
import { authenticate } from "../authentication";

export async function setMembershipRoutes(fastify: FastifyInstance) {
type Schema = {
Body: MembershipData;
Params: {
membershipId: string;
}
};

const params = {
type: "object",
properties: {
membershipId: {
type: "string",
},
},
required: ["membershipId"],
};

const response = {
200: {
description: "Updated membership",
...membershipSchema.membership,
},
};

const schema = {
description: "Update an existing membership",
tags: ["membership"],
summary: "",
body: membershipSchema.membershipData,
response,
params,
security: [
{
apiKey: [] as string[],
},
],
};

try {
fastify.put<Schema>("/:membershipId", {
schema,
preHandler: authenticate(fastify),
async handler(request, response) {
const { membershipId } = request.params;
const existing = await getMembership(membershipId);
const membership = await setMembership({
// Completely replace excluding created at
// createdAt must come from the server
...request.body,
createdAt: existing?.createdAt,
membershipId
});
response.status(200);
response.send(membership);
},
});
} catch {}
}

0 comments on commit d565434

Please sign in to comment.