-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create an interface for output vendors #78
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,19 @@ | ||
import { Transaction } from '@/originalBudgetTrackingApp/bankScraper'; | ||
import { Config } from './configManager/configManager'; | ||
import { Transaction } from './bankScraper'; | ||
|
||
export interface EnrichedTransaction extends Transaction { | ||
accountNumber: string; | ||
category?: string; | ||
hash: string; | ||
} | ||
|
||
export enum OutputVendorName { | ||
YNAB = 'ynab', | ||
GOOGLE_SHEETS = 'googleSheets' | ||
} | ||
|
||
export interface OutputVendor { | ||
name: OutputVendorName; | ||
init?: (config: Config) => Promise<void>; | ||
exportTransactions: (transactionsToCreate: EnrichedTransaction[], startDate: Date, config: Config) => Promise<any>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ import * as bankScraper from './bankScraper'; | |
import { ScaperScrapingResult, Transaction } from './bankScraper'; | ||
import * as ynab from './outputVendors/ynab/ynab'; | ||
import { EnrichedTransaction } from './commonTypes'; | ||
import * as googleSheets from './outputVendors/googleSheets/googleSheets'; | ||
import * as categoryCalculation from './categoryCalculationScript'; | ||
import * as configManager from './configManager/configManager'; | ||
import outputVendors from './outputVendors'; | ||
|
@@ -104,48 +103,23 @@ export function calculateTransactionHash({ | |
} | ||
|
||
async function createTransactionsInExternalVendors(config: Config, companyIdToTransactions: Record<string, EnrichedTransaction[]>, startDate: Date) { | ||
await ynab.init(config); | ||
const activeVendors: any = []; | ||
if (config.outputVendors.ynab?.active) { | ||
activeVendors.push({ | ||
name: 'ynab', | ||
createTransactionFunction: ynab.createTransactions, | ||
options: config.outputVendors.ynab.options, | ||
}); | ||
} | ||
if (config.outputVendors.googleSheets?.active) { | ||
activeVendors.push({ | ||
name: 'googleSheets', | ||
createTransactionFunction: googleSheets.createTransactionsInGoogleSheets, | ||
options: config.outputVendors.googleSheets.options, | ||
}); | ||
} | ||
const executionResult = {}; | ||
const allTransactions = _.flatten(Object.values(companyIdToTransactions)); | ||
|
||
if (!activeVendors.length) { | ||
throw new Error('You need to set at least one output vendor to be active'); | ||
} | ||
|
||
for (let j = 0; j < activeVendors.length; j++) { | ||
const vendor = activeVendors[j]; | ||
const vendorConfig = config.outputVendors[vendor.name]; | ||
if (vendorConfig && vendorConfig.active) { | ||
const vendorResult = await createTransactionsInVedor(vendor, allTransactions, startDate); | ||
executionResult[vendor.name] = vendorResult; | ||
for (let j = 0; j < outputVendors.length; j++) { | ||
const outputVendor = outputVendors[j]; | ||
if (config.outputVendors[outputVendor.name]?.active) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what I meant |
||
await outputVendor.init?.(config); | ||
console.log(`Start creating transactions in ${outputVendor.name}`); | ||
const vendorResult = await outputVendor.exportTransactions(allTransactions, startDate, config); | ||
console.log(`Finished creating transactions in ${outputVendor.name}`); | ||
executionResult[outputVendor.name] = vendorResult; | ||
} | ||
} | ||
return executionResult; | ||
} | ||
|
||
async function createTransactionsInVedor(vendor, transactions: EnrichedTransaction[], startDate: Date) { | ||
console.log(`Start creating transactions in ${vendor.name}`); | ||
const vendorResult = await vendor.createTransactionFunction(transactions, startDate, vendor.options); | ||
if (vendorResult) { | ||
console.log(`${vendor.name} result: `, vendorResult); | ||
if (!Object.keys(executionResult).length) { | ||
throw new Error('You need to set at least one output vendor to be active'); | ||
Comment on lines
+119
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't understand this Error, and now more. I know, there is no meaning of scraping without exporting, because we don't save any state, but still, something feels wired to me. Maybe because I'm running the process without output, for test the scraping, and I know there are no outputs, so why it's error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This error exists because when setting up the project you could easily forget to set any output vendor as active and then it would look as if all went well but actually nothing happened. This type of check should be an indication in the ui instead of an error thrown. I am all for removing it, once we add an indication in the ui thats makes it clear that you have no active exporters defined. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Achla, this is kind of what I mean. |
||
} | ||
console.log(`Finished creating transactions in ${vendor.name}`); | ||
return vendorResult; | ||
return executionResult; | ||
} | ||
|
||
export async function getFinancialAccountNumbers() { | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { ynabOutputVendor } from './ynab/ynab'; | ||
import { googleSheetsOutputVendor } from './googleSheets/googleSheets'; | ||
|
||
const outputVendors = [ynabOutputVendor, googleSheetsOutputVendor]; | ||
|
||
export default outputVendors; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about getting a subset of the whole
Config
type?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats a good question. I considered making the OutputVendor generic and get the type of its own config. The problem there is that the calling code needs to know which config to fetch. This could be solved by assuming that the name of the output vendor will be the name of the output vendor config. To make that work I think we will need to make the output vendor name and enum instead of string. I will take a look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works, but I will need to ts ignore the code that loops over the output vendors and calls export transactions because there the ts compiler is not able to know that we are passing the correct config.
Perhaps if output vendor is a class and would have a base class that has a getVendorConfig function that will return the config for that vendor (generically typed).
Will push a solution with ts ignore and when I do the class implementation will see if the solution with the base class works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's all good, but I'm afraid that I'm over-thinking. I think we can merge it, and I'm sure we will touch this code soon, so maybe the next time we will touch here we will find another solution.
In my head I always thinking about the
israeli-bank-scrapers
implementation- they have an object with the names and the fields, this is like yourenum
andconfig
, and they have afactory
to create the instance ofoutputVendor
(in our case).But I'm sure after we continue to work, if we did something wrong, we will find and fix it.