-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Switch to publishing this automation as a GitHub action
- Loading branch information
1 parent
8e6e818
commit 7317cd8
Showing
57 changed files
with
2,028 additions
and
290 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# In JavaScript actions, `dist/index.js` is a special file. When you reference | ||
# an action with `uses:`, `dist/index.js` is the code that will be run. For this | ||
# project, the `dist/index.js` file is generated from other source files through | ||
# the build process. We need to make sure that the checked-in `dist/index.js` | ||
# file matches what is expected from the build. | ||
# | ||
# This workflow will fail if the checked-in `dist/index.js` file does not match | ||
# what is expected from the build. | ||
name: Check dist/ | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
paths-ignore: | ||
- '**.md' | ||
pull_request: | ||
paths-ignore: | ||
- '**.md' | ||
workflow_dispatch: | ||
|
||
jobs: | ||
check-dist: | ||
name: Check dist/ | ||
runs-on: ubuntu-latest | ||
|
||
permissions: | ||
contents: read | ||
statuses: write | ||
|
||
steps: | ||
- name: Checkout | ||
id: checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: 20 | ||
cache: npm | ||
|
||
- name: Install Dependencies | ||
id: install | ||
run: npm ci | ||
|
||
- name: Build dist/ Directory | ||
id: build | ||
run: npm run build | ||
|
||
- name: Compare Expected and Actual Directories | ||
id: diff | ||
run: | | ||
if [ "$(git diff --ignore-space-at-eol --text dist/ | wc -l)" -gt "0" ]; then | ||
echo "Detected uncommitted changes after build. See status below:" | ||
git diff --ignore-space-at-eol --text dist/ | ||
exit 1 | ||
fi | ||
# If index.js was different than expected, upload the expected version as | ||
# a workflow artifact. | ||
- uses: actions/upload-artifact@v3 | ||
if: ${{ failure() && steps.diff.conclusion == 'failure' }} | ||
with: | ||
name: dist | ||
path: dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: 'You Need a Budget Automation' | ||
description: 'Automate your YNAB budgeting with this GitHub Action, providing automated approval, replication, and stock portfolio tracking.' | ||
author: 'Sierra Softworks' | ||
|
||
# Define your inputs here. | ||
inputs: | ||
budget_id: | ||
description: 'The YNAB budget ID which you would like this action to operate against.' | ||
required: false | ||
default: "default" | ||
api_key: | ||
description: 'The YNAB API key which you would like this action to use. You can find your API key at https://app.ynab.com/settings/developer' | ||
required: true | ||
cache: | ||
description: 'Whether the cache should be enabled for use with this action.' | ||
required: false | ||
default: "true" | ||
|
||
# Define your outputs here. | ||
outputs: | ||
success: | ||
description: 'Indicates whether your YNAB account was updated successfully or not.' | ||
|
||
runs: | ||
using: node20 | ||
main: dist/index.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import * as assert from "assert" | ||
import {AlphaVantage} from "./alphavantage" | ||
|
||
describe.skip("AlphaVantage Stock API", () => { | ||
const alphaVantage = new AlphaVantage(process.env.ALPHAVANTAGE_API_KEY) | ||
|
||
describe("getCurrencyData", () => { | ||
it("should return currency data for a pair of currencies", async () => { | ||
const rate = await alphaVantage.getCurrencyData("USD", "EUR") | ||
|
||
assert.notEqual(rate, 0, "it should return a current price (i.e non-zero)") | ||
}) | ||
}) | ||
|
||
describe("getStockData", () => { | ||
it("should return data for a stock", async () => { | ||
const data = await alphaVantage.getStockData("HCP") | ||
assert.equal(data.symbol, "HCP", "it should return the correct company symbol") | ||
assert.equal(data.currency, "USD", "it should return the correct data") | ||
assert.ok(data.price, "it should return a current price") | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { buildUrl, fetchSafe } from "../utils/http" | ||
import {CurrencyDataSource, DataSource, StockDataSource} from "./datasource" | ||
|
||
export class AlphaVantage extends DataSource implements StockDataSource, CurrencyDataSource { | ||
constructor(protected apiKey: string = process.env.ALPHAVANTAGE_API_KEY) { | ||
super("alphavantage") | ||
} | ||
|
||
async getCompanyData(symbol: string): Promise<CompanyInfo> { | ||
return await this.cached( | ||
`company-${symbol}`, | ||
"year", | ||
() => fetchSafe(buildUrl(companyUrl, { SYMBOL: symbol.toUpperCase(), ALPHAVANTAGE_API_KEY: this.apiKey }))) | ||
} | ||
|
||
async getStockData(symbol: string): Promise<StockQuote> { | ||
/* | ||
The API returns an object with the following structure: | ||
{ | ||
"Global Quote": { | ||
"01. symbol": "IBM", | ||
"02. open": "133.2600", | ||
"03. high": "134.6100", | ||
"04. low": "133.1000", | ||
"05. price": "134.2400", | ||
"06. volume": "3168419", | ||
"07. latest trading day": "2023-07-17", | ||
"08. previous close": "133.4000", | ||
"09. change": "0.8400", | ||
"10. change percent": "0.6297%" | ||
} | ||
} | ||
*/ | ||
|
||
const companyInfo = await this.getCompanyData(symbol.toUpperCase()) | ||
|
||
const response: any = await this.cached( | ||
`stock-${symbol}`, | ||
"day", | ||
() => fetchSafe(buildUrl(stockUrl, { SYMBOL: symbol.toUpperCase(), ALPHAVANTAGE_API_KEY: this.apiKey }))) | ||
|
||
return { | ||
symbol: companyInfo.Symbol, | ||
currency: companyInfo.Currency, | ||
price: parseFloat(response["Global Quote"]["05. price"]) | ||
} | ||
} | ||
|
||
async getCurrencyData(from: string, to: string): Promise<number> { | ||
from = from.toUpperCase() | ||
to = to.toUpperCase() | ||
|
||
/* | ||
The API returns an object with the following structure: | ||
{ | ||
"Realtime Currency Exchange Rate": { | ||
"1. From_Currency Code": "USD", | ||
"2. From_Currency Name": "United States Dollar", | ||
"3. To_Currency Code": "JPY", | ||
"4. To_Currency Name": "Japanese Yen", | ||
"5. Exchange Rate": "138.99100000", | ||
"6. Last Refreshed": "2023-07-18 19:39:02", | ||
"7. Time Zone": "UTC", | ||
"8. Bid Price": "138.98920000", | ||
"9. Ask Price": "138.99750000" | ||
} | ||
} | ||
*/ | ||
|
||
const response: any = await this.cached( | ||
`currency-${from}-${to}`, | ||
"day", | ||
() => fetchSafe(buildUrl(currencyUrl, { FROM_SYMBOL: from.toUpperCase(), TO_SYMBOL: to.toUpperCase(), ALPHAVANTAGE_API_KEY: this.apiKey }))) | ||
|
||
return parseFloat(response["Realtime Currency Exchange Rate"]["5. Exchange Rate"]) | ||
} | ||
} | ||
|
||
const companyUrl = "https://www.alphavantage.co/query?function=OVERVIEW&symbol={SYMBOL}&apikey={ALPHAVANTAGE_API_KEY}" | ||
|
||
const stockUrl = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={SYMBOL}&apikey={ALPHAVANTAGE_API_KEY}" | ||
|
||
const currencyUrl = "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency={FROM_SYMBOL}&to_currency={TO_SYMBOL}&apikey={ALPHAVANTAGE_API_KEY}" | ||
|
||
interface CompanyInfo { | ||
Symbol: string | ||
Name: string | ||
Exchange: string | ||
Currency: string | ||
} | ||
|
||
interface StockQuote { | ||
symbol: string, | ||
currency: string | ||
price: number | ||
} |
Oops, something went wrong.