Skip to content

Commit

Permalink
feat(GraphQL): add transactions import fields on Expenses and Orders
Browse files Browse the repository at this point in the history
  • Loading branch information
Betree committed Jan 17, 2025
1 parent 17d7833 commit 323f6ba
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 1 deletion.
8 changes: 8 additions & 0 deletions server/graphql/common/expenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,14 @@ export const canSeeExpenseDraftPrivateDetails: ExpensePermissionEvaluator = asyn
}
};

export const canSeeExpenseTransactionImportRow: ExpensePermissionEvaluator = async (req, expense) => {
if (!validateExpenseScope(req)) {
return false;
} else {
return isHostAdmin(req, expense);
}
};

/** Checks if the user can verify or resend a draft */
export const canVerifyDraftExpense: ExpensePermissionEvaluator = async (req, expense): Promise<boolean> => {
if (!validateExpenseScope(req)) {
Expand Down
24 changes: 23 additions & 1 deletion server/graphql/common/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import { executeOrder } from '../../lib/payments';
import models, { AccountingCategory, Collective, sequelize, Tier, TransactionsImportRow, User } from '../../models';
import { AccountingCategoryAppliesTo } from '../../models/AccountingCategory';
import Order from '../../models/Order';
import { NotFound, ValidationFailed } from '../errors';
import { Forbidden, NotFound, ValidationFailed } from '../errors';
import { getOrderTaxInfoFromTaxInput } from '../v1/mutations/orders';
import { TaxInput } from '../v2/input/TaxInput';

import { checkScope } from './scope-check';

type AddFundsInput = {
totalAmount: number;
paymentProcessorFee?: number;
Expand Down Expand Up @@ -194,6 +196,26 @@ export const canSeeOrderPrivateActivities = async (req: express.Request, order:
return isOrderHostAdmin(req, order);
};

const validateOrderScope = (req: express.Request, options: { throw?: boolean } = { throw: false }) => {
if (!checkScope(req, 'orders')) {
if (options.throw) {
throw new Forbidden('You do not have the necessary scope to perform this action');
} else {
return false;
}
}

return true;
};

export const canSeeOrderTransactionImportRow = async (req: express.Request, order: Order): Promise<boolean> => {
if (!validateOrderScope(req)) {
return false;
} else {
return isOrderHostAdmin(req, order);
}
};

export const canSetOrderTags = async (req: express.Request, order: Order): Promise<boolean> => {
if (!req.remoteUser) {
return false;
Expand Down
14 changes: 14 additions & 0 deletions server/graphql/loaders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,20 @@ export const loaders = req => {

/** *** TransactionsImports *****/
context.loaders.TransactionsImport.stats = generateTransactionsImportStatsLoader();
context.loaders.TransactionsImportRow.byExpenseId = new DataLoader(async expenseIds => {
const rows = await models.TransactionsImportRow.findAll({
where: { ExpenseId: { [Op.in]: expenseIds } },
});

return sortResultsSimple(expenseIds, rows, 'ExpenseId');
});
context.loaders.TransactionsImportRow.byOrderId = new DataLoader(async orderIds => {
const rows = await models.TransactionsImportRow.findAll({
where: { OrderId: { [Op.in]: orderIds } },
});

return sortResultsSimple(orderIds, rows, 'OrderId');
});

context.loaders.search = generateSearchLoaders(req);

Expand Down
15 changes: 15 additions & 0 deletions server/graphql/schemaV2.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2086,6 +2086,11 @@ type Order {
offset: Int! = 0
orderBy: ChronologicalOrderInput = { field: CREATED_AT, direction: ASC }
): CommentCollection

"""
[Host admins only] If the order was associated with a transactions import row, this field will reference it
"""
transactionImportRow: TransactionsImportRow

Check notice on line 2093 in server/graphql/schemaV2.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector - Schema v2

Field 'transactionImportRow' was added to object type 'Order'

Field 'transactionImportRow' was added to object type 'Order'
}

"""
Expand Down Expand Up @@ -2584,6 +2589,11 @@ type Expense {
Fields that cannot be edited on this expense
"""
lockedFields: [ExpenseLockableFields]

"""
[Host admins only] If the expense associated with a transactions import row, this field will reference it
"""
transactionImportRow: TransactionsImportRow

Check notice on line 2596 in server/graphql/schemaV2.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector - Schema v2

Field 'transactionImportRow' was added to object type 'Expense'

Field 'transactionImportRow' was added to object type 'Expense'
}

"""
Expand Down Expand Up @@ -4715,6 +4725,11 @@ enum LastCommentBy {
"""
HOST_ADMIN

"""
Not a Fiscal Host Admin
"""
NON_HOST_ADMIN

Check warning on line 4731 in server/graphql/schemaV2.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector - Schema v2

Enum value 'NON_HOST_ADMIN' was added to enum 'LastCommentBy'

Adding an enum value may break existing clients that were not programming defensively against an added case when querying an enum.

"""
Collective Admin
"""
Expand Down
11 changes: 11 additions & 0 deletions server/graphql/v2/object/Expense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import GraphQLPayoutMethod from './PayoutMethod';
import GraphQLRecurringExpense from './RecurringExpense';
import { GraphQLSecurityCheck } from './SecurityCheck';
import { GraphQLTaxInfo } from './TaxInfo';
import { GraphQLTransactionsImportRow } from './TransactionsImportRow';
import { GraphQLTransferWiseRequiredField } from './TransferWise';
import { GraphQLVirtualCard } from './VirtualCard';

Expand Down Expand Up @@ -736,6 +737,16 @@ export const GraphQLExpense = new GraphQLObjectType<ExpenseModel, express.Reques
return expense.data?.lockedFields || [];
},
},
transactionImportRow: {
type: GraphQLTransactionsImportRow,
description:
'[Host admins only] If the expense associated with a transactions import row, this field will reference it',
async resolve(expense, _, req) {
if (await ExpenseLib.canSeeExpenseTransactionImportRow(req, expense)) {
return req.loaders.TransactionsImportRow.byExpenseId.load(expense.id);
}
},
},
};
},
});
11 changes: 11 additions & 0 deletions server/graphql/v2/object/Order.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { GraphQLMemberOf } from './Member';
import GraphQLOrderPermissions from './OrderPermissions';
import { GraphQLOrderTax } from './OrderTax';
import { GraphQLTaxInfo } from './TaxInfo';
import { GraphQLTransactionsImportRow } from './TransactionsImportRow';

const GraphQLPendingOrderFromAccountInfo = new GraphQLObjectType({
name: 'PendingOrderFromAccountInfo',
Expand Down Expand Up @@ -499,6 +500,16 @@ export const GraphQLOrder = new GraphQLObjectType({
};
},
},
transactionImportRow: {
type: GraphQLTransactionsImportRow,
description:
'[Host admins only] If the order was associated with a transactions import row, this field will reference it',
async resolve(order, _, req) {
if (await OrdersLib.canSeeOrderTransactionImportRow(req, order)) {
return req.loaders.TransactionsImportRow.byOrderId.load(order.id);
}
},
},
};
},
});

0 comments on commit 323f6ba

Please sign in to comment.