Remove some any types from the API (#3238)

* Remove some `any` types from the API

* Add release notes

* No backwards-incompatible changes

* Update tests to reflect API changes

* PR feedback: standardize on id for deletes

* PR feedback: restore partial updates
This commit is contained in:
Julian Dominguez-Schatz
2024-08-14 13:28:09 -04:00
committed by GitHub
parent 63ad6dadf2
commit d9066a49c4
10 changed files with 53 additions and 42 deletions

View File

@@ -81,28 +81,22 @@ describe('API CRUD operations', () => {
expect(groups).toEqual(
expect.arrayContaining([
expect.objectContaining({
hidden: 0,
hidden: false,
id: 'fc3825fd-b982-4b72-b768-5b30844cf832',
is_income: 0,
is_income: false,
name: 'Usual Expenses',
sort_order: 16384,
tombstone: 0,
}),
expect.objectContaining({
hidden: 0,
hidden: false,
id: 'a137772f-cf2f-4089-9432-822d2ddc1466',
is_income: 0,
is_income: false,
name: 'Investments and Savings',
sort_order: 32768,
tombstone: 0,
}),
expect.objectContaining({
hidden: 0,
hidden: false,
id: '2E1F5BDB-209B-43F9-AF2C-3CE28E380C00',
is_income: 1,
is_income: true,
name: 'Income',
sort_order: 32768,
tombstone: 0,
}),
]),
);
@@ -563,10 +557,10 @@ describe('API CRUD operations', () => {
);
// delete rules
await api.deleteRule(rules[1]);
await api.deleteRule(rules[1].id);
expect(await api.getRules()).toHaveLength(1);
await api.deleteRule(rules[0]);
await api.deleteRule(rules[0].id);
expect(await api.getRules()).toHaveLength(0);
});

View File

@@ -205,8 +205,8 @@ export function updateRule(rule) {
return send('api/rule-update', { rule });
}
export function deleteRule(id) {
return send('api/rule-delete', { id });
export function deleteRule(id: string) {
return send('api/rule-delete', id);
}
export function holdBudgetForNextMonth(month, amount) {

View File

@@ -209,7 +209,7 @@ describe('Transaction rules', () => {
expect(transaction.payee).toBe('Kroger');
expect(transaction.category).toBe('food');
await deleteRule({ id });
await deleteRule(id);
expect(getRules().length).toBe(0);
transaction = runRules({
payee: 'Kroger',

View File

@@ -223,16 +223,17 @@ export async function updateRule(rule) {
return db.update('rules', ruleModel.fromJS(rule));
}
export async function deleteRule<T extends { id: string }>(rule: T) {
export async function deleteRule(id: string) {
const schedule = await db.first('SELECT id FROM schedules WHERE rule = ?', [
rule.id,
id,
]);
if (schedule) {
return false;
}
return db.delete_('rules', rule.id);
await db.delete_('rules', id);
return true;
}
// Sync projections

View File

@@ -44,12 +44,14 @@ let IMPORT_MODE = false;
// we also need to notify the UI manually if stuff has changed (if
// they are connecting to an already running instance, the UI should
// update). The wrapper handles that.
function withMutation(handler) {
return args => {
function withMutation<Params extends Array<unknown>, ReturnType>(
handler: (...args: Params) => Promise<ReturnType>,
) {
return (...args: Params) => {
return runMutator(
async () => {
const latestTimestamp = getClock().timestamp.toString();
const result = await handler(args);
const result = await handler(...args);
const rows = await db.all(
'SELECT DISTINCT dataset FROM messages_crdt WHERE timestamp > ?',
@@ -464,7 +466,7 @@ handlers['api/transactions-add'] = withMutation(async function ({
runTransfers,
learnCategories,
});
return 'ok';
return 'ok' as const;
});
handlers['api/transactions-get'] = async function ({
@@ -503,7 +505,7 @@ handlers['api/transaction-update'] = withMutation(async function ({
}
const { diff } = updateTransaction(transactions, { id, ...fields });
return handlers['transactions-batch-update'](diff);
return handlers['transactions-batch-update'](diff)['updated'];
});
handlers['api/transaction-delete'] = withMutation(async function ({ id }) {
@@ -518,7 +520,7 @@ handlers['api/transaction-delete'] = withMutation(async function ({ id }) {
}
const { diff } = deleteTransaction(transactions, id);
return handlers['transactions-batch-update'](diff);
return handlers['transactions-batch-update'](diff)['deleted'];
});
handlers['api/accounts-get'] = async function () {
@@ -590,7 +592,8 @@ handlers['api/categories-get'] = async function ({
handlers['api/category-groups-get'] = async function () {
checkFileOpen();
return handlers['get-category-groups']();
const groups = await handlers['get-category-groups']();
return groups.map(categoryGroupModel.toExternal);
};
handlers['api/category-group-create'] = withMutation(async function ({
@@ -711,7 +714,7 @@ handlers['api/rule-create'] = withMutation(async function ({ rule }) {
handlers['api/rule-update'] = withMutation(async function ({ rule }) {
checkFileOpen();
const updatedRule = handlers['rule-update'](rule);
const updatedRule = await handlers['rule-update'](rule);
if ('error' in updatedRule) {
throw APIError('Failed updating the rule', updatedRule.error);
@@ -720,7 +723,7 @@ handlers['api/rule-update'] = withMutation(async function ({ rule }) {
return updatedRule;
});
handlers['api/rule-delete'] = withMutation(async function ({ id }) {
handlers['api/rule-delete'] = withMutation(async function (id) {
checkFileOpen();
return handlers['rule-delete'](id);
});

View File

@@ -111,8 +111,8 @@ app.method(
app.method(
'rule-delete',
mutator(async function (rule) {
return rules.deleteRule(rule);
mutator(async function (id) {
return rules.deleteRule(id);
}),
);
@@ -123,7 +123,7 @@ app.method(
await batchMessages(async () => {
for (const id of ids) {
const res = await rules.deleteRule({ id });
const res = await rules.deleteRule(id);
if (res === false) {
someDeletionsFailed = true;
}

View File

@@ -18,13 +18,16 @@ export interface RulesHandlers {
'rule-add': (
rule: Omit<RuleEntity, 'id'>,
) => Promise<{ error: ValidationError } | { id: string }>;
) => Promise<{ error: ValidationError } | RuleEntity>;
'rule-update': (
rule: Partial<RuleEntity>,
) => Promise<{ error: ValidationError } | object>;
'rule-update': <
PartialRule extends Partial<Omit<RuleEntity, 'id'>> &
Pick<RuleEntity, 'id'>,
>(
rule: PartialRule,
) => Promise<{ error: ValidationError } | PartialRule>;
'rule-delete': (rule: Required<RuleEntity>) => Promise<false | void>;
'rule-delete': (id: string) => Promise<boolean>;
'rule-delete-all': (
ids: string[],

View File

@@ -66,7 +66,7 @@ export interface ApiHandlers {
'api/budget-hold-for-next-month': (arg: {
month: string;
amount: number;
}) => Promise<void>;
}) => Promise<boolean>;
'api/budget-reset-hold': (arg: { month: string }) => Promise<void>;
@@ -76,7 +76,11 @@ export interface ApiHandlers {
payees;
}) => Promise<unknown>;
'api/transactions-import': (arg: { accountId; transactions }) => Promise<{
'api/transactions-import': (arg: {
accountId;
transactions;
isPreview?;
}) => Promise<{
errors?: { message: string }[];
added;
updated;
@@ -102,7 +106,7 @@ export interface ApiHandlers {
'api/transaction-delete': (arg: {
id;
}) => Promise<Awaited<ReturnType<typeof batchUpdateTransactions>>['updated']>;
}) => Promise<Awaited<ReturnType<typeof batchUpdateTransactions>>['deleted']>;
'api/sync': () => Promise<void>;
@@ -176,5 +180,5 @@ export interface ApiHandlers {
'api/rule-update': (arg: { rule: RuleEntity }) => Promise<RuleEntity>;
'api/rule-delete': (arg: { id: string }) => Promise<boolean>;
'api/rule-delete': (id: string) => Promise<boolean>;
}

View File

@@ -108,7 +108,7 @@ export interface ServerHandlers {
'payees-get-rule-counts': () => Promise<unknown>;
'payees-merge': (arg: { targetId; mergeIds }) => Promise<unknown>;
'payees-merge': (arg: { targetId; mergeIds }) => Promise<void>;
'payees-batch-change': (arg: {
added?;

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [jfdoming]
---
Remove some `any` types from the API