Skip to content
This repository has been archived by the owner on Aug 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #277 from beabee-communityrm/feat/payment-api
Browse files Browse the repository at this point in the history
Add a basic read only payment API
  • Loading branch information
wpf500 authored Jun 29, 2023
2 parents 92427c5 + ece7492 commit 8d8a0f9
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 25 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"test": "jest --setupFiles dotenv/config"
},
"dependencies": {
"@beabee/beabee-common": "^1.13.1",
"@beabee/beabee-common": "^1.13.2",
"@sendgrid/mail": "^7.7.0",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
Expand Down
2 changes: 2 additions & 0 deletions src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ContentController } from "./controllers/ContentController";
import { EmailController } from "./controllers/EmailController";
import { ContactController } from "./controllers/ContactController";
import { NoticeController } from "./controllers/NoticeController";
import { PaymentController } from "./controllers/PaymentController";
import { SegmentController } from "./controllers/SegmentController";
import { SignupController } from "./controllers/SignupController";
import { StatsController } from "./controllers/StatsController";
Expand Down Expand Up @@ -104,6 +105,7 @@ db.connect().then(() => {
EmailController,
ContactController,
NoticeController,
PaymentController,
SegmentController,
SignupController,
StatsController,
Expand Down
7 changes: 3 additions & 4 deletions src/api/controllers/ContactController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ import PaymentService from "@core/services/PaymentService";
import { ContributionInfo } from "@core/utils";
import { generatePassword } from "@core/utils/auth";

import JoinFlow from "@models/JoinFlow";
import Contact from "@models/Contact";
import ContactProfile from "@models/ContactProfile";
import Payment from "@models/Payment";
import ContactRole from "@models/ContactRole";
import JoinFlow from "@models/JoinFlow";
import Payment from "@models/Payment";

import { UUIDParam } from "@api/data";
import {
Expand All @@ -53,8 +53,6 @@ import {
GetContactRoleData,
GetContactsQuery,
GetContactWith,
GetPaymentData,
GetPaymentsQuery,
UpdateContactRoleData,
UpdateContactData,
exportContacts
Expand All @@ -75,6 +73,7 @@ import {
Paginated,
GetExportQuery
} from "@api/data/PaginatedData";
import { GetPaymentData, GetPaymentsQuery } from "@api/data/PaymentData";

import PartialBody from "@api/decorators/PartialBody";
import CantUpdateContribution from "@api/errors/CantUpdateContribution";
Expand Down
40 changes: 40 additions & 0 deletions src/api/controllers/PaymentController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Paginated, fetchPaginated, mergeRules } from "@api/data/PaginatedData";
import {
Authorized,
CurrentUser,
Get,
JsonController,
QueryParams
} from "routing-controllers";

import {
GetPaymentData,
GetPaymentsQuery,
fetchPaginatedPayments
} from "@api/data/PaymentData";

import Contact from "@models/Contact";

@JsonController("/payment")
@Authorized()
export class PaymentController {
@Get("/")
async getPayments(
@CurrentUser() contact: Contact,
@QueryParams() query: GetPaymentsQuery
): Promise<Paginated<GetPaymentData>> {
const authedQuery = {
...query,
rules: mergeRules([
query.rules,
// Non-admins can only see their own payments
!contact.hasRole("admin") && {
field: "contact",
operator: "equal",
value: [contact.id]
}
])
};
return await fetchPaginatedPayments(authedQuery, contact);
}
}
13 changes: 0 additions & 13 deletions src/api/data/ContactData/interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
ContributionPeriod,
NewsletterStatus,
PaymentStatus,
RoleType,
RoleTypes
} from "@beabee/beabee-common";
Expand Down Expand Up @@ -213,15 +212,3 @@ export class CreateContactData extends UpdateContactData {
@Type(() => CreateContactRoleData)
roles?: CreateContactRoleData[];
}

export interface GetPaymentData {
amount: number;
chargeDate: Date;
status: PaymentStatus;
}

const paymentSortFields = ["amount", "chargeDate"] as const;
export class GetPaymentsQuery extends GetPaginatedQuery {
@IsIn(paymentSortFields)
sort?: string;
}
53 changes: 53 additions & 0 deletions src/api/data/PaymentData/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Payment from "@models/Payment";
import { GetPaymentData, GetPaymentWith, GetPaymentsQuery } from "./interface";
import { Paginated, paymentFilters } from "@beabee/beabee-common";
import { fetchPaginated } from "../PaginatedData";
import Contact from "@models/Contact";
import { convertContactToData, loadContactRoles } from "../ContactData";

function convertPaymentToPaymentData(
payment: Payment,
_with?: GetPaymentWith[]
): GetPaymentData {
return {
amount: payment.amount,
chargeDate: payment.chargeDate,
status: payment.status,
...(_with?.includes(GetPaymentWith.Contact) && {
contact: payment.contact && convertContactToData(payment.contact)
})
};
}

export async function fetchPaginatedPayments(
query: GetPaymentsQuery,
contact: Contact
): Promise<Paginated<GetPaymentData>> {
const data = await fetchPaginated(
Payment,
paymentFilters,
query,
contact,
undefined,
(qb, fieldPrefix) => {
if (query.with?.includes(GetPaymentWith.Contact)) {
qb.leftJoinAndSelect(`${fieldPrefix}contact`, "contact");
}
}
);

// Load contact roles after to ensure offset/limit work
const contacts = data.items
.map((item) => item.contact)
.filter((c) => !!c) as Contact[];
await loadContactRoles(contacts);

return {
...data,
items: data.items.map((item) =>
convertPaymentToPaymentData(item, query.with)
)
};
}

export * from "./interface";
27 changes: 27 additions & 0 deletions src/api/data/PaymentData/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PaymentStatus } from "@beabee/beabee-common";
import { IsArray, IsEnum, IsIn, IsOptional } from "class-validator";

import { GetPaginatedQuery } from "../PaginatedData";
import { GetContactData } from "../ContactData";

export interface GetPaymentData {
amount: number;
chargeDate: Date;
status: PaymentStatus;
contact?: GetContactData | null;
}

export enum GetPaymentWith {
Contact = "contact"
}

const paymentSortFields = ["amount", "chargeDate"] as const;
export class GetPaymentsQuery extends GetPaginatedQuery {
@IsArray()
@IsOptional()
@IsEnum(GetPaymentWith, { each: true })
with?: GetPaymentWith[];

@IsIn(paymentSortFields)
sort?: string;
}

0 comments on commit 8d8a0f9

Please sign in to comment.