{"id":13626434,"url":"https://github.com/eric-bach/pecuniary","last_synced_at":"2025-04-16T14:33:31.770Z","repository":{"id":38922711,"uuid":"229629983","full_name":"eric-bach/pecuniary","owner":"eric-bach","description":"Pecuniary web application built with AWS CDK","archived":false,"fork":false,"pushed_at":"2024-05-22T15:27:11.000Z","size":10323,"stargazers_count":12,"open_issues_count":4,"forks_count":5,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-05-22T20:24:01.042Z","etag":null,"topics":["aws","cdk","dynamodb","graphql","react","serverless"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eric-bach.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-22T20:57:02.000Z","updated_at":"2024-05-27T19:59:27.801Z","dependencies_parsed_at":"2024-01-14T08:17:47.793Z","dependency_job_id":"da7affc6-595a-4932-95a4-5becd82d48cb","html_url":"https://github.com/eric-bach/pecuniary","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric-bach%2Fpecuniary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric-bach%2Fpecuniary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric-bach%2Fpecuniary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric-bach%2Fpecuniary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eric-bach","download_url":"https://codeload.github.com/eric-bach/pecuniary/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223716516,"owners_count":17191058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","cdk","dynamodb","graphql","react","serverless"],"created_at":"2024-08-01T21:02:18.472Z","updated_at":"2025-04-16T14:33:31.756Z","avatar_url":"https://github.com/eric-bach.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cp align=\"center\"\u003e\n    \u003cimg src=\"references/diagrams/icon.png\" height=\"28\" width=\"28\" alt=\"icon\"\u003e\n    Pecuniary\n  \u003c/p\u003e\n\u003c/h1\u003e\n\n[![Build](https://github.com/eric-bach/pecuniary/actions/workflows/test.yml/badge.svg)](https://github.com/eric-bach/pecuniary/actions/workflows/test.yml)\n[![Deploy](https://github.com/eric-bach/pecuniary/actions/workflows/deploy.yml/badge.svg)](https://github.com/eric-bach/pecuniary/actions/workflows/deploy.yml)\n[![CodeFactor](https://www.codefactor.io/repository/github/eric-bach/pecuniary/badge)](https://www.codefactor.io/repository/github/eric-bach/pecuniary)\n[![GitHub issues open](https://img.shields.io/github/issues/eric-bach/pecuniary.svg?maxAge=2592000)](https://github.com/eric-bach/pecuniary/issues?q=is%3Aopen+is%3Aissue) [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/eric-bach/pecuniary.svg?maxAge=2592000)](https://github.com/eric-bach/pecuniary/issues?q=is%3Aissue+is%3Aclosed)\n[![Chat](https://img.shields.io/gitter/room/pecuniary/community)](https://gitter.im/pecuniary/community) ![license](https://img.shields.io/badge/license-MIT-blue.svg)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e |\n  \u003ca href=\"ARCHITECTURE.md\"\u003eArchitecture\u003c/a\u003e |\n  \u003ca href=\"#deployment\"\u003eDevelopment\u003c/a\u003e |\n  \u003ca href=\"https://trello.com/b/7lA2gwTs/pecuniary\"\u003eTrello\u003c/a\u003e |\n  \u003ca href=\"#project-resources\"\u003eResources\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  An \u003cstrong\u003eevent-driven serverless microservices\u003c/strong\u003e financial investment application built with \u003ca href=\"https://nextjs.org\"\u003eNext.js\u003c/a\u003e and \u003ca href=\"https://tailwindcss.com\"\u003eTailwind CSS\u003c/a\u003e on \u003ca href=\"https://aws.amazon.com\"\u003eAmazon Web Services\u003c/a\u003e\n\u003c/p\u003e\n\n## Architecture\n\n![Top Level](references/diagrams/toplevel.jpg)\n\n⚠️\u003cstrong\u003eNote:\u003c/strong\u003e the Event Sourcing and CQRS version is no longer maintained. Please see \u003ca href=\"https://github.com/eric-bach/pecuniary/tree/cqrs-v1\"\u003ebranch\u003c/a\u003e and \u003ca href=\"https://github.com/eric-bach/pecuniary/blob/cqrs-v1/ARCHITECTURE.md\"\u003eCQRS Architecture\u003c/a\u003e for the last release.\n\n## Getting Started\n\nThis quick start guide describes how to get the application running. An `AWS account` is required to deploy the infrastructure required for this project.\n\n### Configure the app\n\n1.  Clone the project\n\n    ```bash\n    $ git clone https://github.com/eric-bach/pecuniary.git\n    ```\n\n2.  Install dependencies for CDK\n\n    ```bash\n    $ cd ./infrastructure\n    $ npm install\n    ```\n\n3.  Install dependencies for the rest of the application using the recursive-install script\n\n    ```bash\n    $ cd ./infrastructure\n    $ npm cinstall-all\n    ```\n\n4.  Copy the `./infrastructure/.env.example` file to `./infrastructure/.env` and fill in the parameter values (if the app has not been deployed to AWS yet, the ARN will be empty for now):\n\n    ```\n    DLQ_NOTIFICATIONS - email address to send failed event message notifications to.\n    AWS_SERVICE_ROLE_ARN - ARN of the GitHub actions role to deploy the stack.\n    ```\n\n5.  Copy the `./frontend/.env.example` file to `./frontend/.env` and fill in the parameter values from the CDK stack outputs in step 2:\n\n    ```\n    NEXT_PUBLIC_USER_POOL_ID=\n    NEXT_PUBLIC_USER_POOL_CLIENT_ID=\n    NEXT_PUBLIC_APPSYNC_API_ENDPOINT=\n    NEXT_PUBLIC_APPSYNC_REGION=\n    ```\n\n## Deploy the app\n\n1.  Follow the steps in [Deployment with CDK CLI](#deployment-with-cdk-cli)\n\n## Running the app locally\n\n1.  Start the frontend:\n\n    ```bash\n    $ cd frontend\n    $ npm run dev\n    ```\n\n# Deployment\n\n## Deployment with CDK CLI\n\nThe Pecuniary application consists of the CDK backend and React frontend, each of which has an independent method of deploying. The backend is deployed via CDK where the frontend is deployed via SST.\n\n### Deploy backend via CDK script\n\n1. Bootstrap CDK (one-time only)\n\n   ```\n   $ cdk bootstrap aws://{ACCOUNT_ID}/{REGION} --profile {PROFILE_NAME}}\n   ```\n\n2. Ensure AWS credentials are up to date. If using AWS SSO, authorize a set of temporary credentials\n\n   ```bash\n   aws sso login --profile PROFILE_NAME\n   ```\n\n3. Deploy the backend and frontend\n\n   ```\n   $ npm run deploy dev PROFILE_NAME\n   ```\n\n## Deployment via GitHub Actions\n\n1. Create an AWS role that can be assumed by GitHub Actions\n\n   ```\n   $ npm run deploy-cicd prod PROFILE_NAME\n   ```\n\n2. Add the following GitHub Secrets to the repository\n\n   Common\n\n   ```\n   CDK_DEFAULT_REGION - AWS default region for all resources to be created\n   ```\n\n   Dev environment\n\n   ```\n   CLOUDFRONT_DOMAIN_DEV - AWS CloudFront Distribution domain name\n   COGNITO_USERPOOL_ID_DEV - Cognito User Pool Id\n   COGNITO_WEB_CLIENT_ID_DEV - Cognito User Pool Client Id\n   APPSYNC_ENDPOINT_DEV - AWS AppSync GraphQL endpoint URL\n   CYPRESS_USERNAME - A valid Cognito username for Cypress integration testing\n   CYPRESS_PASSWORD - The Cognito password for Cypress integration testing\n   ```\n\n   Production environment\n\n   ```\n   AWS_SERVICE_ROLE_PROD - AWS ARN of the GitHub Actions Role to Assume (from step 1)\n   CERTIFICATE_ARN - ARN to ACM certificate for CloudFront Distribution\n   HOSTED_ZONE_ID - Route53 Hosted Zone ID\n   HOSTED_ZONE_NAME - Route53 Hosted Zone Name\n   DLQ_NOTIFICATIONS - email address to send DLQ messages to\n   CLOUDFRONT_DOMAIN_PROD - AWS CloudFront Distribution domain name\n   COGNITO_USERPOOL_ID_PROD - Cognito User Pool Id\n   COGNITO_WEB_CLIENT_ID_PROD - Cognito User Pool Client Id\n   APPSYNC_ENDPOINT_PROD - AWS AppSync GraphQL endpoint URL\n   ```\n\n# Testing\n\n## Jest\n\nTo be added\n\n## Cypress\n\nTo be added\n\n### Locally\n\nTest the frontend using cypress\n\n```\n$ npm run cypress:open\n\nor headless,\n\n$ npm run cypress:run\n```\n\n### GitHub Actions\n\nEnsure to configure the GitHub Secrets to include:\n\n    ```\n    CYPRESS_USERNAME - A valid Cognito username for Cypress integration testing\n    CYPRESS_PASSWORD - The Cognito password for Cypress integration testing\n    ```\n\n# Troubleshooting\n\n## Lambda DLQ\n\nTo retry the Lambda function\n\n1. Get the message from the SQS DLQ\n\n```\naws sqs receive-message --queue-url https://sqs.us-east-1.amazonaws.com/524849261220/pecuniary-dev-updatePosition-DLQ --profile bach-dev\n```\n\n2. Save the DLQ message event to a json file\n\n```\n{\n  \"version\": \"0\",\n  \"id\": \"26b8f7d1-b141-9baf-8a01-f625b7c3b0bc\",\n  \"detail-type\": \"InvestmentTransactionSavedEvent\",\n  \"source\": \"custom.pecuniary\",\n  \"account\": \"524849261220\",\n  \"time\": \"2024-12-31T20:29:49Z\",\n  \"region\": \"us-east-1\",\n  \"resources\": [],\n  \"detail\": {\n      \"accountId\": \"c8df14ba-0bf9-43dc-a92f-0185714336a7\",\n      \"transactionDate\": \"2024-12-31\",\n      \"type\": \"Buy\",\n      \"symbol\": \"FNMA\",\n      \"shares\": 123.0,\n      \"price\": 12.0,\n      \"commission\": 12.0,\n      \"userId\": \"a4e824d8-2021-7008-6807-ec5d9400debb\"\n  }\n}\n```\n\n3. Run the CLI command to invoke lambda\n\n```\naws lambda invoke --function-name pecuniary-dev-UpdatePosition --payload fileb://request.json response.json --profile bach-dev\n```\n\n4. Run the CLI command to remove the message from the SQS DLQ\n\n```\naws sqs delete-message --queue-url https://sqs.us-east-1.amazonaws.com/524849261220/pecuniary-dev-updatePosition-DLQ --receipt-handle \u003creceipt-handle\u003e --profile bach-dev\n```\n\n# Event Sourcing and CQRS Architecture\n\nFor more detailed information about the event-driven nature of the Pecuniary application and it's architecture, please see the [Architecture.md](ARCHITECTURE.md)\n\n# Projects References\n\nLinks to additional documentation specific to the Application\n\n## AppSync\n\n- [Saved GraphQL queries/mutations for GraphiQl](docs/GraphQL.md)\n- [How to add a new GraphQL API/Command Handler](docs/CommandHandler.md)\n- [How to call an authenticated AppSync GraphQL API with Apollo Client](docs/ApolloClient.md)\n\n# Resources\n\nVarious links to additional articles/tutorials used to build this application.\n\n## AppSync\n\n- [How to build an authenticated GraphQL AppSync API with CDK](https://github.com/dabit3/build-an-authenticated-api-with-cdk)\n- [How to perform a GraphQL Query with Apollo using React hooks](https://www.yannisspyrou.com/querying-app-sync-using-react-hooks)\n- [How to perform a GraphQL Mutation with Apollo using React hooks](https://www.qualityology.com/tech/connect-to-existing-aws-appsync-api-from-a-react-application/)\n\n## Cognito\n\n- [How to add a protected route to React](https://dev.to/olumidesamuel_/implementing-protected-route-and-authentication-in-react-js-3cl4)\n- [How to authenticate with Cognito using React hooks](https://github.com/DevAscend/YT-AWS-Cognito-React-Tutorials)\n- [How to add a user to a Cognito User Group](https://bobbyhadz.com/blog/aws-cognito-add-user-to-group)\n\n# License\n\nThis project is licensed under the terms of the [MIT license](/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric-bach%2Fpecuniary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feric-bach%2Fpecuniary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric-bach%2Fpecuniary/lists"}