├── .dockerignore ├── .eslintrc.json ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── Data ├── allFuncs │ ├── allCostMock.ts │ ├── allErrorsMock.ts │ └── allInvocationsMock.ts └── byFunc │ ├── costMock.ts │ ├── dummyData.ts │ ├── durationMock.ts │ ├── errorMessagesMock.ts │ ├── errorsMock.ts │ ├── funcsToFix.ts │ ├── invocationsMock.ts │ ├── lossesMock.ts │ ├── memUsageMock.ts │ ├── memUsedVsAllo.ts │ ├── mostErrorsMock.ts │ ├── slowestFuncsMock.ts │ └── throttlesMock.ts ├── Dockerfile ├── Dockerrun.aws.json ├── LICENSE.md ├── README.md ├── __tests__ ├── backend │ └── server.test.js └── supertest.ts ├── containers ├── app.Dockerfile ├── eb.Dockerfile ├── tsbuild.Dockerfile └── webpack.Dockerfile ├── context ├── @types │ └── data.d.ts ├── AppReducer.tsx ├── dataContext.tsx └── userContext.tsx ├── docker-compose-dev.yml ├── docker-compose-test.yml ├── jest-teardown.js ├── jest.config.ts ├── jest.setup.ts ├── package-lock.json ├── package.json ├── scripts ├── deploy-eb.sh └── deploy.sh ├── server ├── auth │ └── auth.ts ├── controllers │ ├── aws │ │ ├── analysisController.ts │ │ ├── cacheController.ts │ │ ├── cloudwatchController.ts │ │ ├── costController.ts │ │ ├── credController.ts │ │ ├── formatController.ts │ │ ├── lambdaController.ts │ │ ├── logController.ts │ │ ├── stepFuncs │ │ │ ├── formatCW.ts │ │ │ └── stepController.ts │ │ ├── stsController.ts │ │ └── utilController.ts │ ├── cookieController.ts │ └── userController.ts ├── data │ └── testData.json ├── database │ └── db.ts ├── routers │ ├── awsRouter.ts │ └── userRouter.ts ├── server.ts └── types.ts ├── src ├── App.tsx ├── animations │ ├── BlobGeometry.jsx │ ├── Butterfly.jsx │ ├── Flower.jsx │ ├── Grass.jsx │ ├── Particles.jsx │ ├── WindLayer.js │ └── tree.glb ├── components │ ├── BarFnGraph.tsx │ ├── ErrorTable.tsx │ ├── FnGraph.tsx │ ├── FnGraphCompare.tsx │ ├── FnSelector.tsx │ ├── MemFnSelector.tsx │ ├── MemReduction.tsx │ ├── Navbar.tsx │ ├── StackedBarFnGraph.tsx │ ├── TimeButtons.tsx │ ├── login-btn.tsx │ ├── signup-btn.tsx │ └── splash-menu.tsx ├── fetchHelper.tsx ├── globals.tsx ├── index.html ├── index.tsx ├── pages │ ├── Dashboard.tsx │ ├── Functions.tsx │ ├── Login.tsx │ ├── Memory.tsx │ ├── Register.tsx │ └── Splash.tsx ├── public │ ├── dashboard.gif │ ├── functions.gif │ ├── memory.gif │ └── sync.gif └── styles.tsx ├── tsconfig.build.json ├── tsconfig.json └── webpack.config.cjs /.dockerignore: -------------------------------------------------------------------------------- 1 | .next 2 | eslintrc.json 3 | .gitattributes 4 | .gitignore 5 | cloudformation.yml 6 | README.md 7 | 8 | zip/ 9 | aws/ 10 | containers/ 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | 4 | # Dependencies 5 | node_modules 6 | 7 | # Environments 8 | .env 9 | 10 | # Builds 11 | dist 12 | build 13 | 14 | build 15 | 16 | .next 17 | # AWS Configs 18 | cloudformation.yml 19 | 20 | # AWS request & response data 21 | /aws/ 22 | 23 | #Ignore from next 24 | # dependencies 25 | /.pnp 26 | .pnp.js 27 | 28 | # testing 29 | /coverage 30 | 31 | # next.js 32 | /.next/ 33 | /out/ 34 | 35 | # production 36 | /build 37 | 38 | # Mac OS 39 | .DS_Store 40 | 41 | # Private Keys 42 | *.pem 43 | 44 | # debug 45 | npm-debug.log* 46 | yarn-debug.log* 47 | yarn-error.log* 48 | .pnpm-debug.log* 49 | 50 | # local env files 51 | .env*.local 52 | 53 | # vercel 54 | .vercel 55 | 56 | # tests 57 | /__tests__/cacheController.test.ts 58 | /__tests__/user.test.ts 59 | 60 | # Elastic Beanstalk Builds 61 | zip 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - docker 3 | dist: xenial 4 | script: 5 | - docker-compose -f docker-compose-test.yml up --abort-on-container-exit 6 | - python3 -VV 7 | - pip install --upgrade pip 8 | - pip -V 9 | before_deploy: 10 | - python3 -m pip install --user awscli 11 | - python3 -m pip install --user awsebcli 12 | - export PATH=$PATH:$HOME/.local/bin 13 | env: 14 | global: PATH=/opt/python/3.7.1/bin:$PATH 15 | deploy: 16 | provider: script 17 | skip_cleanup: true 18 | on: 19 | branch: master 20 | script: sh $TRAVIS_BUILD_DIR/scripts/deploy-eb.sh 21 | -------------------------------------------------------------------------------- /Data/allFuncs/allCostMock.ts: -------------------------------------------------------------------------------- 1 | export const allCostMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | value: 6, 8 | }, 9 | { 10 | month: 'January', 11 | day: 2, 12 | hour: 0, 13 | minute: 0, 14 | value: 5, 15 | }, 16 | { 17 | month: 'January', 18 | day: 3, 19 | hour: 0, 20 | minute: 0, 21 | value: 8, 22 | }, 23 | { 24 | month: 'January', 25 | day: 4, 26 | hour: 0, 27 | minute: 0, 28 | value: 7, 29 | }, 30 | { 31 | month: 'January', 32 | day: 5, 33 | hour: 0, 34 | minute: 0, 35 | value: 1, 36 | }, 37 | { 38 | month: 'January', 39 | day: 6, 40 | hour: 0, 41 | minute: 0, 42 | value: 3, 43 | }, 44 | { 45 | month: 'January', 46 | day: 7, 47 | hour: 0, 48 | minute: 0, 49 | value: 0, 50 | }, 51 | { 52 | month: 'January', 53 | day: 8, 54 | hour: 0, 55 | minute: 0, 56 | value: 1, 57 | }, 58 | { 59 | month: 'January', 60 | day: 9, 61 | hour: 0, 62 | minute: 0, 63 | value: 1, 64 | }, 65 | { 66 | month: 'January', 67 | day: 10, 68 | hour: 0, 69 | minute: 0, 70 | value: 3, 71 | }, 72 | { 73 | month: 'January', 74 | day: 11, 75 | hour: 0, 76 | minute: 0, 77 | value: 9, 78 | }, 79 | { 80 | month: 'January', 81 | day: 12, 82 | hour: 0, 83 | minute: 0, 84 | value: 2, 85 | }, 86 | { 87 | month: 'January', 88 | day: 13, 89 | hour: 0, 90 | minute: 0, 91 | value: 2, 92 | }, 93 | { 94 | month: 'January', 95 | day: 14, 96 | hour: 0, 97 | minute: 0, 98 | value: 3, 99 | }, 100 | { 101 | month: 'January', 102 | day: 15, 103 | hour: 0, 104 | minute: 0, 105 | value: 8, 106 | }, 107 | { 108 | month: 'January', 109 | day: 16, 110 | hour: 0, 111 | minute: 0, 112 | value: 9, 113 | }, 114 | { 115 | month: 'January', 116 | day: 17, 117 | hour: 0, 118 | minute: 0, 119 | value: 4, 120 | }, 121 | { 122 | month: 'January', 123 | day: 18, 124 | hour: 0, 125 | minute: 0, 126 | value: 2, 127 | }, 128 | { 129 | month: 'January', 130 | day: 19, 131 | hour: 0, 132 | minute: 0, 133 | value: 5, 134 | }, 135 | { 136 | month: 'January', 137 | day: 20, 138 | hour: 0, 139 | minute: 0, 140 | value: 9, 141 | }, 142 | { 143 | month: 'January', 144 | day: 21, 145 | hour: 0, 146 | minute: 0, 147 | value: 6, 148 | }, 149 | { 150 | month: 'January', 151 | day: 22, 152 | hour: 0, 153 | minute: 0, 154 | value: 4, 155 | }, 156 | { 157 | month: 'January', 158 | day: 23, 159 | hour: 0, 160 | minute: 0, 161 | value: 8, 162 | }, 163 | { 164 | month: 'January', 165 | day: 24, 166 | hour: 0, 167 | minute: 0, 168 | value: 4, 169 | }, 170 | { 171 | month: 'January', 172 | day: 25, 173 | hour: 0, 174 | minute: 0, 175 | value: 3, 176 | }, 177 | { 178 | month: 'January', 179 | day: 26, 180 | hour: 0, 181 | minute: 0, 182 | value: 0, 183 | }, 184 | { 185 | month: 'January', 186 | day: 27, 187 | hour: 0, 188 | minute: 0, 189 | value: 2, 190 | }, 191 | { 192 | month: 'January', 193 | day: 28, 194 | hour: 0, 195 | minute: 0, 196 | value: 3, 197 | }, 198 | { 199 | month: 'January', 200 | day: 29, 201 | hour: 0, 202 | minute: 0, 203 | value: 7, 204 | }, 205 | { 206 | month: 'January', 207 | day: 30, 208 | hour: 0, 209 | minute: 0, 210 | value: 5, 211 | }, 212 | { 213 | month: 'January', 214 | day: 31, 215 | hour: 0, 216 | minute: 0, 217 | value: 4, 218 | }, 219 | ]; 220 | -------------------------------------------------------------------------------- /Data/allFuncs/allErrorsMock.ts: -------------------------------------------------------------------------------- 1 | export const allErrorsMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | value: 0, 8 | }, 9 | { 10 | month: 'January', 11 | day: 2, 12 | hour: 0, 13 | minute: 0, 14 | value: 1, 15 | }, 16 | { 17 | month: 'January', 18 | day: 3, 19 | hour: 0, 20 | minute: 0, 21 | value: 0, 22 | }, 23 | { 24 | month: 'January', 25 | day: 4, 26 | hour: 0, 27 | minute: 0, 28 | value: 1, 29 | }, 30 | { 31 | month: 'January', 32 | day: 5, 33 | hour: 0, 34 | minute: 0, 35 | value: 1, 36 | }, 37 | { 38 | month: 'January', 39 | day: 6, 40 | hour: 0, 41 | minute: 0, 42 | value: 1, 43 | }, 44 | { 45 | month: 'January', 46 | day: 7, 47 | hour: 0, 48 | minute: 0, 49 | value: 0, 50 | }, 51 | { 52 | month: 'January', 53 | day: 8, 54 | hour: 0, 55 | minute: 0, 56 | value: 1, 57 | }, 58 | { 59 | month: 'January', 60 | day: 9, 61 | hour: 0, 62 | minute: 0, 63 | value: 0, 64 | }, 65 | { 66 | month: 'January', 67 | day: 10, 68 | hour: 0, 69 | minute: 0, 70 | value: 1, 71 | }, 72 | { 73 | month: 'January', 74 | day: 11, 75 | hour: 0, 76 | minute: 0, 77 | value: 2, 78 | }, 79 | { 80 | month: 'January', 81 | day: 12, 82 | hour: 0, 83 | minute: 0, 84 | value: 1, 85 | }, 86 | { 87 | month: 'January', 88 | day: 13, 89 | hour: 0, 90 | minute: 0, 91 | value: 2, 92 | }, 93 | { 94 | month: 'January', 95 | day: 14, 96 | hour: 0, 97 | minute: 0, 98 | value: 1, 99 | }, 100 | { 101 | month: 'January', 102 | day: 15, 103 | hour: 0, 104 | minute: 0, 105 | value: 0, 106 | }, 107 | { 108 | month: 'January', 109 | day: 16, 110 | hour: 0, 111 | minute: 0, 112 | value: 0, 113 | }, 114 | { 115 | month: 'January', 116 | day: 17, 117 | hour: 0, 118 | minute: 0, 119 | value: 2, 120 | }, 121 | { 122 | month: 'January', 123 | day: 18, 124 | hour: 0, 125 | minute: 0, 126 | value: 1, 127 | }, 128 | { 129 | month: 'January', 130 | day: 19, 131 | hour: 0, 132 | minute: 0, 133 | value: 1, 134 | }, 135 | { 136 | month: 'January', 137 | day: 20, 138 | hour: 0, 139 | minute: 0, 140 | value: 2, 141 | }, 142 | { 143 | month: 'January', 144 | day: 21, 145 | hour: 0, 146 | minute: 0, 147 | value: 1, 148 | }, 149 | { 150 | month: 'January', 151 | day: 22, 152 | hour: 0, 153 | minute: 0, 154 | value: 0, 155 | }, 156 | { 157 | month: 'January', 158 | day: 23, 159 | hour: 0, 160 | minute: 0, 161 | value: 2, 162 | }, 163 | { 164 | month: 'January', 165 | day: 24, 166 | hour: 0, 167 | minute: 0, 168 | value: 0, 169 | }, 170 | { 171 | month: 'January', 172 | day: 25, 173 | hour: 0, 174 | minute: 0, 175 | value: 2, 176 | }, 177 | { 178 | month: 'January', 179 | day: 26, 180 | hour: 0, 181 | minute: 0, 182 | value: 0, 183 | }, 184 | { 185 | month: 'January', 186 | day: 27, 187 | hour: 0, 188 | minute: 0, 189 | value: 0, 190 | }, 191 | { 192 | month: 'January', 193 | day: 28, 194 | hour: 0, 195 | minute: 0, 196 | value: 1, 197 | }, 198 | { 199 | month: 'January', 200 | day: 29, 201 | hour: 0, 202 | minute: 0, 203 | value: 0, 204 | }, 205 | { 206 | month: 'January', 207 | day: 30, 208 | hour: 0, 209 | minute: 0, 210 | value: 2, 211 | }, 212 | { 213 | month: 'January', 214 | day: 31, 215 | hour: 0, 216 | minute: 0, 217 | value: 1, 218 | }, 219 | ]; 220 | -------------------------------------------------------------------------------- /Data/allFuncs/allInvocationsMock.ts: -------------------------------------------------------------------------------- 1 | export const allInvocationsMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | value: 6, 8 | }, 9 | { 10 | month: 'January', 11 | day: 2, 12 | hour: 0, 13 | minute: 0, 14 | value: 5, 15 | }, 16 | { 17 | month: 'January', 18 | day: 3, 19 | hour: 0, 20 | minute: 0, 21 | value: 8, 22 | }, 23 | { 24 | month: 'January', 25 | day: 4, 26 | hour: 0, 27 | minute: 0, 28 | value: 7, 29 | }, 30 | { 31 | month: 'January', 32 | day: 5, 33 | hour: 0, 34 | minute: 0, 35 | value: 1, 36 | }, 37 | { 38 | month: 'January', 39 | day: 6, 40 | hour: 0, 41 | minute: 0, 42 | value: 3, 43 | }, 44 | { 45 | month: 'January', 46 | day: 7, 47 | hour: 0, 48 | minute: 0, 49 | value: 0, 50 | }, 51 | { 52 | month: 'January', 53 | day: 8, 54 | hour: 0, 55 | minute: 0, 56 | value: 1, 57 | }, 58 | { 59 | month: 'January', 60 | day: 9, 61 | hour: 0, 62 | minute: 0, 63 | value: 1, 64 | }, 65 | { 66 | month: 'January', 67 | day: 10, 68 | hour: 0, 69 | minute: 0, 70 | value: 3, 71 | }, 72 | { 73 | month: 'January', 74 | day: 11, 75 | hour: 0, 76 | minute: 0, 77 | value: 9, 78 | }, 79 | { 80 | month: 'January', 81 | day: 12, 82 | hour: 0, 83 | minute: 0, 84 | value: 2, 85 | }, 86 | { 87 | month: 'January', 88 | day: 13, 89 | hour: 0, 90 | minute: 0, 91 | value: 2, 92 | }, 93 | { 94 | month: 'January', 95 | day: 14, 96 | hour: 0, 97 | minute: 0, 98 | value: 3, 99 | }, 100 | { 101 | month: 'January', 102 | day: 15, 103 | hour: 0, 104 | minute: 0, 105 | value: 8, 106 | }, 107 | { 108 | month: 'January', 109 | day: 16, 110 | hour: 0, 111 | minute: 0, 112 | value: 9, 113 | }, 114 | { 115 | month: 'January', 116 | day: 17, 117 | hour: 0, 118 | minute: 0, 119 | value: 4, 120 | }, 121 | { 122 | month: 'January', 123 | day: 18, 124 | hour: 0, 125 | minute: 0, 126 | value: 2, 127 | }, 128 | { 129 | month: 'January', 130 | day: 19, 131 | hour: 0, 132 | minute: 0, 133 | value: 5, 134 | }, 135 | { 136 | month: 'January', 137 | day: 20, 138 | hour: 0, 139 | minute: 0, 140 | value: 9, 141 | }, 142 | { 143 | month: 'January', 144 | day: 21, 145 | hour: 0, 146 | minute: 0, 147 | value: 6, 148 | }, 149 | { 150 | month: 'January', 151 | day: 22, 152 | hour: 0, 153 | minute: 0, 154 | value: 4, 155 | }, 156 | { 157 | month: 'January', 158 | day: 23, 159 | hour: 0, 160 | minute: 0, 161 | value: 8, 162 | }, 163 | { 164 | month: 'January', 165 | day: 24, 166 | hour: 0, 167 | minute: 0, 168 | value: 4, 169 | }, 170 | { 171 | month: 'January', 172 | day: 25, 173 | hour: 0, 174 | minute: 0, 175 | value: 3, 176 | }, 177 | { 178 | month: 'January', 179 | day: 26, 180 | hour: 0, 181 | minute: 0, 182 | value: 0, 183 | }, 184 | { 185 | month: 'January', 186 | day: 27, 187 | hour: 0, 188 | minute: 0, 189 | value: 2, 190 | }, 191 | { 192 | month: 'January', 193 | day: 28, 194 | hour: 0, 195 | minute: 0, 196 | value: 3, 197 | }, 198 | { 199 | month: 'January', 200 | day: 29, 201 | hour: 0, 202 | minute: 0, 203 | value: 7, 204 | }, 205 | { 206 | month: 'January', 207 | day: 30, 208 | hour: 0, 209 | minute: 0, 210 | value: 5, 211 | }, 212 | { 213 | month: 'January', 214 | day: 31, 215 | hour: 0, 216 | minute: 0, 217 | value: 4, 218 | }, 219 | ]; 220 | -------------------------------------------------------------------------------- /Data/byFunc/dummyData.ts: -------------------------------------------------------------------------------- 1 | export const dummyData = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 6, 8 | AccumulusFunc2: 1, 9 | AccumulusFunc3: 8, 10 | AccumulusFunc4: 6, 11 | AccumulusFunc5: 0, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 6, 19 | AccumulusFunc2: 7, 20 | AccumulusFunc3: 1, 21 | AccumulusFunc4: 7, 22 | AccumulusFunc5: 5, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 4, 30 | AccumulusFunc2: 4, 31 | AccumulusFunc3: 4, 32 | AccumulusFunc4: 1, 33 | AccumulusFunc5: 5, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 6, 41 | AccumulusFunc2: 8, 42 | AccumulusFunc3: 5, 43 | AccumulusFunc4: 2, 44 | AccumulusFunc5: 6, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 5, 52 | AccumulusFunc2: 7, 53 | AccumulusFunc3: 4, 54 | AccumulusFunc4: 4, 55 | AccumulusFunc5: 1, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 3, 63 | AccumulusFunc2: 3, 64 | AccumulusFunc3: 6, 65 | AccumulusFunc4: 6, 66 | AccumulusFunc5: 1, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 1, 74 | AccumulusFunc2: 8, 75 | AccumulusFunc3: 8, 76 | AccumulusFunc4: 6, 77 | AccumulusFunc5: 3, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 2, 85 | AccumulusFunc2: 5, 86 | AccumulusFunc3: 9, 87 | AccumulusFunc4: 6, 88 | AccumulusFunc5: 6, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 6, 96 | AccumulusFunc2: 1, 97 | AccumulusFunc3: 5, 98 | AccumulusFunc4: 4, 99 | AccumulusFunc5: 5, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 8, 107 | AccumulusFunc2: 8, 108 | AccumulusFunc3: 3, 109 | AccumulusFunc4: 7, 110 | AccumulusFunc5: 7, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 5, 118 | AccumulusFunc2: 3, 119 | AccumulusFunc3: 0, 120 | AccumulusFunc4: 1, 121 | AccumulusFunc5: 2, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 1, 129 | AccumulusFunc2: 3, 130 | AccumulusFunc3: 6, 131 | AccumulusFunc4: 1, 132 | AccumulusFunc5: 5, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 5, 140 | AccumulusFunc2: 1, 141 | AccumulusFunc3: 9, 142 | AccumulusFunc4: 5, 143 | AccumulusFunc5: 0, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 2, 151 | AccumulusFunc2: 1, 152 | AccumulusFunc3: 4, 153 | AccumulusFunc4: 2, 154 | AccumulusFunc5: 2, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 0, 162 | AccumulusFunc2: 5, 163 | AccumulusFunc3: 5, 164 | AccumulusFunc4: 8, 165 | AccumulusFunc5: 2, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 7, 173 | AccumulusFunc2: 3, 174 | AccumulusFunc3: 4, 175 | AccumulusFunc4: 9, 176 | AccumulusFunc5: 0, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 6, 184 | AccumulusFunc2: 9, 185 | AccumulusFunc3: 0, 186 | AccumulusFunc4: 2, 187 | AccumulusFunc5: 7, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 1, 195 | AccumulusFunc2: 6, 196 | AccumulusFunc3: 3, 197 | AccumulusFunc4: 6, 198 | AccumulusFunc5: 7, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 5, 206 | AccumulusFunc2: 0, 207 | AccumulusFunc3: 7, 208 | AccumulusFunc4: 2, 209 | AccumulusFunc5: 0, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 8, 217 | AccumulusFunc2: 0, 218 | AccumulusFunc3: 8, 219 | AccumulusFunc4: 7, 220 | AccumulusFunc5: 3, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 7, 228 | AccumulusFunc2: 8, 229 | AccumulusFunc3: 5, 230 | AccumulusFunc4: 6, 231 | AccumulusFunc5: 3, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 9, 239 | AccumulusFunc2: 7, 240 | AccumulusFunc3: 3, 241 | AccumulusFunc4: 5, 242 | AccumulusFunc5: 1, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 2, 250 | AccumulusFunc2: 1, 251 | AccumulusFunc3: 0, 252 | AccumulusFunc4: 9, 253 | AccumulusFunc5: 4, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 3, 261 | AccumulusFunc2: 3, 262 | AccumulusFunc3: 6, 263 | AccumulusFunc4: 0, 264 | AccumulusFunc5: 1, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 0, 272 | AccumulusFunc2: 0, 273 | AccumulusFunc3: 6, 274 | AccumulusFunc4: 2, 275 | AccumulusFunc5: 5, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 0, 283 | AccumulusFunc2: 2, 284 | AccumulusFunc3: 2, 285 | AccumulusFunc4: 0, 286 | AccumulusFunc5: 5, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 0, 294 | AccumulusFunc2: 8, 295 | AccumulusFunc3: 4, 296 | AccumulusFunc4: 2, 297 | AccumulusFunc5: 0, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 3, 305 | AccumulusFunc2: 8, 306 | AccumulusFunc3: 6, 307 | AccumulusFunc4: 7, 308 | AccumulusFunc5: 4, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 2, 316 | AccumulusFunc2: 4, 317 | AccumulusFunc3: 0, 318 | AccumulusFunc4: 8, 319 | AccumulusFunc5: 2, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 2, 327 | AccumulusFunc2: 2, 328 | AccumulusFunc3: 7, 329 | AccumulusFunc4: 0, 330 | AccumulusFunc5: 1, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 6, 338 | AccumulusFunc2: 8, 339 | AccumulusFunc3: 7, 340 | AccumulusFunc4: 5, 341 | AccumulusFunc5: 3, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Data/byFunc/durationMock.ts: -------------------------------------------------------------------------------- 1 | export const durationMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 2, 8 | AccumulusFunc2: 0, 9 | AccumulusFunc3: 9, 10 | AccumulusFunc4: 8, 11 | AccumulusFunc5: 8, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 7, 19 | AccumulusFunc2: 9, 20 | AccumulusFunc3: 7, 21 | AccumulusFunc4: 4, 22 | AccumulusFunc5: 0, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 5, 30 | AccumulusFunc2: 2, 31 | AccumulusFunc3: 3, 32 | AccumulusFunc4: 6, 33 | AccumulusFunc5: 5, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 8, 41 | AccumulusFunc2: 2, 42 | AccumulusFunc3: 1, 43 | AccumulusFunc4: 3, 44 | AccumulusFunc5: 2, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 5, 52 | AccumulusFunc2: 1, 53 | AccumulusFunc3: 4, 54 | AccumulusFunc4: 2, 55 | AccumulusFunc5: 5, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 6, 63 | AccumulusFunc2: 7, 64 | AccumulusFunc3: 3, 65 | AccumulusFunc4: 3, 66 | AccumulusFunc5: 0, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 5, 74 | AccumulusFunc2: 1, 75 | AccumulusFunc3: 9, 76 | AccumulusFunc4: 0, 77 | AccumulusFunc5: 1, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 9, 85 | AccumulusFunc2: 5, 86 | AccumulusFunc3: 1, 87 | AccumulusFunc4: 8, 88 | AccumulusFunc5: 3, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 0, 96 | AccumulusFunc2: 3, 97 | AccumulusFunc3: 1, 98 | AccumulusFunc4: 1, 99 | AccumulusFunc5: 2, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 5, 107 | AccumulusFunc2: 5, 108 | AccumulusFunc3: 3, 109 | AccumulusFunc4: 4, 110 | AccumulusFunc5: 3, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 9, 118 | AccumulusFunc2: 6, 119 | AccumulusFunc3: 4, 120 | AccumulusFunc4: 0, 121 | AccumulusFunc5: 4, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 7, 129 | AccumulusFunc2: 1, 130 | AccumulusFunc3: 3, 131 | AccumulusFunc4: 1, 132 | AccumulusFunc5: 1, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 9, 140 | AccumulusFunc2: 0, 141 | AccumulusFunc3: 1, 142 | AccumulusFunc4: 3, 143 | AccumulusFunc5: 9, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 6, 151 | AccumulusFunc2: 1, 152 | AccumulusFunc3: 7, 153 | AccumulusFunc4: 0, 154 | AccumulusFunc5: 4, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 5, 162 | AccumulusFunc2: 7, 163 | AccumulusFunc3: 7, 164 | AccumulusFunc4: 4, 165 | AccumulusFunc5: 5, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 5, 173 | AccumulusFunc2: 3, 174 | AccumulusFunc3: 9, 175 | AccumulusFunc4: 9, 176 | AccumulusFunc5: 8, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 7, 184 | AccumulusFunc2: 7, 185 | AccumulusFunc3: 0, 186 | AccumulusFunc4: 1, 187 | AccumulusFunc5: 8, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 4, 195 | AccumulusFunc2: 6, 196 | AccumulusFunc3: 4, 197 | AccumulusFunc4: 2, 198 | AccumulusFunc5: 2, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 0, 206 | AccumulusFunc2: 1, 207 | AccumulusFunc3: 5, 208 | AccumulusFunc4: 0, 209 | AccumulusFunc5: 0, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 6, 217 | AccumulusFunc2: 0, 218 | AccumulusFunc3: 4, 219 | AccumulusFunc4: 3, 220 | AccumulusFunc5: 4, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 3, 228 | AccumulusFunc2: 8, 229 | AccumulusFunc3: 7, 230 | AccumulusFunc4: 0, 231 | AccumulusFunc5: 5, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 0, 239 | AccumulusFunc2: 0, 240 | AccumulusFunc3: 6, 241 | AccumulusFunc4: 0, 242 | AccumulusFunc5: 4, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 0, 250 | AccumulusFunc2: 8, 251 | AccumulusFunc3: 6, 252 | AccumulusFunc4: 4, 253 | AccumulusFunc5: 2, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 2, 261 | AccumulusFunc2: 1, 262 | AccumulusFunc3: 6, 263 | AccumulusFunc4: 2, 264 | AccumulusFunc5: 8, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 2, 272 | AccumulusFunc2: 4, 273 | AccumulusFunc3: 3, 274 | AccumulusFunc4: 1, 275 | AccumulusFunc5: 2, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 1, 283 | AccumulusFunc2: 8, 284 | AccumulusFunc3: 8, 285 | AccumulusFunc4: 0, 286 | AccumulusFunc5: 8, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 3, 294 | AccumulusFunc2: 4, 295 | AccumulusFunc3: 1, 296 | AccumulusFunc4: 5, 297 | AccumulusFunc5: 8, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 0, 305 | AccumulusFunc2: 5, 306 | AccumulusFunc3: 8, 307 | AccumulusFunc4: 3, 308 | AccumulusFunc5: 8, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 5, 316 | AccumulusFunc2: 8, 317 | AccumulusFunc3: 7, 318 | AccumulusFunc4: 7, 319 | AccumulusFunc5: 1, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 3, 327 | AccumulusFunc2: 0, 328 | AccumulusFunc3: 3, 329 | AccumulusFunc4: 5, 330 | AccumulusFunc5: 0, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 9, 338 | AccumulusFunc2: 3, 339 | AccumulusFunc3: 5, 340 | AccumulusFunc4: 5, 341 | AccumulusFunc5: 5, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Data/byFunc/errorMessagesMock.ts: -------------------------------------------------------------------------------- 1 | export const errorMessagesMock = [ 2 | { 3 | function: 'AccumulusFunc3', 4 | year: 2022, 5 | month: 'January', 6 | logs: [ 7 | { 8 | id: 1, 9 | date: '2022-01-21T19:35:30.000Z', 10 | message: 11 | "[ERROR] NameError: name 'prin' is not defined Traceback (most recent call last): File '/var/task/lambda_function.py', line 10, in lambda_handler prin('value3 = ' + event['key3']) [ERROR] NameError: name 'prin' is not defined Traceback (most recent call last):", 12 | }, 13 | { 14 | id: 2, 15 | date: '2022-01-02T19:35:30.000Z', 16 | message: 'Delegation not found', 17 | }, 18 | ], 19 | }, 20 | { 21 | function: 'AccumulusFunc1', 22 | year: 2022, 23 | month: 'January', 24 | logs: [ 25 | { 26 | id: 1, 27 | date: '2022-01-11T19:35:30.000Z', 28 | message: 'ARN does not exist for user', 29 | }, 30 | { 31 | id: 2, 32 | date: '2022-01-09T19:35:30.000Z', 33 | message: 'InvalidParamterCombination', 34 | }, 35 | ], 36 | }, 37 | { 38 | function: 'AccumulusFunc4', 39 | year: 2022, 40 | month: 'January', 41 | logs: [], 42 | }, 43 | { 44 | function: 'AccumulusFunc2', 45 | year: 2022, 46 | month: 'January', 47 | logs: [], 48 | }, 49 | { 50 | function: 'AccumulusFunc5', 51 | year: 2022, 52 | month: 'January', 53 | logs: [ 54 | { 55 | id: 1, 56 | date: '2022-01-21T19:35:30.000Z', 57 | message: 'Did not find a handler for event', 58 | }, 59 | { 60 | id: 2, 61 | date: '2022-01-12T19:35:30.000Z', 62 | message: 'Unauthorized request by user. Execution halted.', 63 | }, 64 | { 65 | id: 3, 66 | date: '2022-01-26T19:35:30.000Z', 67 | message: 'Unauthorized request by user. Execution halted.', 68 | }, 69 | ], 70 | }, 71 | ]; 72 | -------------------------------------------------------------------------------- /Data/byFunc/errorsMock.ts: -------------------------------------------------------------------------------- 1 | export const errorsMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 0, 8 | AccumulusFunc2: 1, 9 | AccumulusFunc3: 2, 10 | AccumulusFunc4: 0, 11 | AccumulusFunc5: 2, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 1, 19 | AccumulusFunc2: 1, 20 | AccumulusFunc3: 0, 21 | AccumulusFunc4: 1, 22 | AccumulusFunc5: 1, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 0, 30 | AccumulusFunc2: 1, 31 | AccumulusFunc3: 2, 32 | AccumulusFunc4: 2, 33 | AccumulusFunc5: 0, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 1, 41 | AccumulusFunc2: 0, 42 | AccumulusFunc3: 0, 43 | AccumulusFunc4: 0, 44 | AccumulusFunc5: 0, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 1, 52 | AccumulusFunc2: 2, 53 | AccumulusFunc3: 1, 54 | AccumulusFunc4: 2, 55 | AccumulusFunc5: 1, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 1, 63 | AccumulusFunc2: 0, 64 | AccumulusFunc3: 1, 65 | AccumulusFunc4: 1, 66 | AccumulusFunc5: 0, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 0, 74 | AccumulusFunc2: 1, 75 | AccumulusFunc3: 1, 76 | AccumulusFunc4: 1, 77 | AccumulusFunc5: 2, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 1, 85 | AccumulusFunc2: 1, 86 | AccumulusFunc3: 2, 87 | AccumulusFunc4: 1, 88 | AccumulusFunc5: 0, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 0, 96 | AccumulusFunc2: 1, 97 | AccumulusFunc3: 0, 98 | AccumulusFunc4: 2, 99 | AccumulusFunc5: 0, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 1, 107 | AccumulusFunc2: 0, 108 | AccumulusFunc3: 1, 109 | AccumulusFunc4: 1, 110 | AccumulusFunc5: 0, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 2, 118 | AccumulusFunc2: 2, 119 | AccumulusFunc3: 2, 120 | AccumulusFunc4: 2, 121 | AccumulusFunc5: 1, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 1, 129 | AccumulusFunc2: 2, 130 | AccumulusFunc3: 0, 131 | AccumulusFunc4: 2, 132 | AccumulusFunc5: 0, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 2, 140 | AccumulusFunc2: 1, 141 | AccumulusFunc3: 0, 142 | AccumulusFunc4: 0, 143 | AccumulusFunc5: 0, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 1, 151 | AccumulusFunc2: 0, 152 | AccumulusFunc3: 1, 153 | AccumulusFunc4: 0, 154 | AccumulusFunc5: 2, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 0, 162 | AccumulusFunc2: 0, 163 | AccumulusFunc3: 1, 164 | AccumulusFunc4: 1, 165 | AccumulusFunc5: 1, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 0, 173 | AccumulusFunc2: 0, 174 | AccumulusFunc3: 2, 175 | AccumulusFunc4: 1, 176 | AccumulusFunc5: 0, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 2, 184 | AccumulusFunc2: 1, 185 | AccumulusFunc3: 0, 186 | AccumulusFunc4: 1, 187 | AccumulusFunc5: 0, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 1, 195 | AccumulusFunc2: 2, 196 | AccumulusFunc3: 1, 197 | AccumulusFunc4: 0, 198 | AccumulusFunc5: 2, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 1, 206 | AccumulusFunc2: 2, 207 | AccumulusFunc3: 0, 208 | AccumulusFunc4: 2, 209 | AccumulusFunc5: 0, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 2, 217 | AccumulusFunc2: 0, 218 | AccumulusFunc3: 0, 219 | AccumulusFunc4: 2, 220 | AccumulusFunc5: 0, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 1, 228 | AccumulusFunc2: 0, 229 | AccumulusFunc3: 0, 230 | AccumulusFunc4: 0, 231 | AccumulusFunc5: 2, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 0, 239 | AccumulusFunc2: 2, 240 | AccumulusFunc3: 1, 241 | AccumulusFunc4: 2, 242 | AccumulusFunc5: 1, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 2, 250 | AccumulusFunc2: 2, 251 | AccumulusFunc3: 2, 252 | AccumulusFunc4: 2, 253 | AccumulusFunc5: 0, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 0, 261 | AccumulusFunc2: 2, 262 | AccumulusFunc3: 1, 263 | AccumulusFunc4: 1, 264 | AccumulusFunc5: 2, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 2, 272 | AccumulusFunc2: 1, 273 | AccumulusFunc3: 1, 274 | AccumulusFunc4: 2, 275 | AccumulusFunc5: 1, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 0, 283 | AccumulusFunc2: 1, 284 | AccumulusFunc3: 2, 285 | AccumulusFunc4: 1, 286 | AccumulusFunc5: 1, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 0, 294 | AccumulusFunc2: 0, 295 | AccumulusFunc3: 1, 296 | AccumulusFunc4: 1, 297 | AccumulusFunc5: 2, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 1, 305 | AccumulusFunc2: 1, 306 | AccumulusFunc3: 2, 307 | AccumulusFunc4: 2, 308 | AccumulusFunc5: 2, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 0, 316 | AccumulusFunc2: 0, 317 | AccumulusFunc3: 2, 318 | AccumulusFunc4: 2, 319 | AccumulusFunc5: 1, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 2, 327 | AccumulusFunc2: 1, 328 | AccumulusFunc3: 1, 329 | AccumulusFunc4: 1, 330 | AccumulusFunc5: 0, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 1, 338 | AccumulusFunc2: 0, 339 | AccumulusFunc3: 0, 340 | AccumulusFunc4: 0, 341 | AccumulusFunc5: 2, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Data/byFunc/funcsToFix.ts: -------------------------------------------------------------------------------- 1 | export const funcsToFix = [ 2 | { 3 | name: 'AccumulusFunc5', 4 | value: 500, 5 | }, 6 | { 7 | name: 'AccumulusFunc3', 8 | value: 400, 9 | }, 10 | { 11 | name: 'AccumulusFunc2', 12 | value: 300, 13 | }, 14 | ]; 15 | -------------------------------------------------------------------------------- /Data/byFunc/invocationsMock.ts: -------------------------------------------------------------------------------- 1 | export const invocationsMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 6, 8 | AccumulusFunc2: 4, 9 | AccumulusFunc3: 8, 10 | AccumulusFunc4: 3, 11 | AccumulusFunc5: 6, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 5, 19 | AccumulusFunc2: 1, 20 | AccumulusFunc3: 2, 21 | AccumulusFunc4: 3, 22 | AccumulusFunc5: 5, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 8, 30 | AccumulusFunc2: 4, 31 | AccumulusFunc3: 6, 32 | AccumulusFunc4: 7, 33 | AccumulusFunc5: 4, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 7, 41 | AccumulusFunc2: 1, 42 | AccumulusFunc3: 9, 43 | AccumulusFunc4: 8, 44 | AccumulusFunc5: 6, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 1, 52 | AccumulusFunc2: 2, 53 | AccumulusFunc3: 8, 54 | AccumulusFunc4: 9, 55 | AccumulusFunc5: 8, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 3, 63 | AccumulusFunc2: 4, 64 | AccumulusFunc3: 9, 65 | AccumulusFunc4: 8, 66 | AccumulusFunc5: 7, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 0, 74 | AccumulusFunc2: 3, 75 | AccumulusFunc3: 4, 76 | AccumulusFunc4: 5, 77 | AccumulusFunc5: 8, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 1, 85 | AccumulusFunc2: 3, 86 | AccumulusFunc3: 2, 87 | AccumulusFunc4: 4, 88 | AccumulusFunc5: 6, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 1, 96 | AccumulusFunc2: 4, 97 | AccumulusFunc3: 0, 98 | AccumulusFunc4: 3, 99 | AccumulusFunc5: 9, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 3, 107 | AccumulusFunc2: 7, 108 | AccumulusFunc3: 2, 109 | AccumulusFunc4: 6, 110 | AccumulusFunc5: 2, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 9, 118 | AccumulusFunc2: 7, 119 | AccumulusFunc3: 3, 120 | AccumulusFunc4: 6, 121 | AccumulusFunc5: 2, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 2, 129 | AccumulusFunc2: 7, 130 | AccumulusFunc3: 6, 131 | AccumulusFunc4: 5, 132 | AccumulusFunc5: 1, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 2, 140 | AccumulusFunc2: 1, 141 | AccumulusFunc3: 5, 142 | AccumulusFunc4: 9, 143 | AccumulusFunc5: 3, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 3, 151 | AccumulusFunc2: 8, 152 | AccumulusFunc3: 7, 153 | AccumulusFunc4: 5, 154 | AccumulusFunc5: 1, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 8, 162 | AccumulusFunc2: 6, 163 | AccumulusFunc3: 2, 164 | AccumulusFunc4: 2, 165 | AccumulusFunc5: 8, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 9, 173 | AccumulusFunc2: 8, 174 | AccumulusFunc3: 1, 175 | AccumulusFunc4: 2, 176 | AccumulusFunc5: 0, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 4, 184 | AccumulusFunc2: 1, 185 | AccumulusFunc3: 0, 186 | AccumulusFunc4: 7, 187 | AccumulusFunc5: 1, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 2, 195 | AccumulusFunc2: 0, 196 | AccumulusFunc3: 6, 197 | AccumulusFunc4: 1, 198 | AccumulusFunc5: 3, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 5, 206 | AccumulusFunc2: 9, 207 | AccumulusFunc3: 9, 208 | AccumulusFunc4: 6, 209 | AccumulusFunc5: 4, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 9, 217 | AccumulusFunc2: 6, 218 | AccumulusFunc3: 7, 219 | AccumulusFunc4: 1, 220 | AccumulusFunc5: 4, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 6, 228 | AccumulusFunc2: 9, 229 | AccumulusFunc3: 8, 230 | AccumulusFunc4: 6, 231 | AccumulusFunc5: 4, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 4, 239 | AccumulusFunc2: 1, 240 | AccumulusFunc3: 7, 241 | AccumulusFunc4: 5, 242 | AccumulusFunc5: 5, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 8, 250 | AccumulusFunc2: 8, 251 | AccumulusFunc3: 7, 252 | AccumulusFunc4: 0, 253 | AccumulusFunc5: 2, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 4, 261 | AccumulusFunc2: 6, 262 | AccumulusFunc3: 6, 263 | AccumulusFunc4: 5, 264 | AccumulusFunc5: 8, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 3, 272 | AccumulusFunc2: 2, 273 | AccumulusFunc3: 0, 274 | AccumulusFunc4: 8, 275 | AccumulusFunc5: 1, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 0, 283 | AccumulusFunc2: 3, 284 | AccumulusFunc3: 7, 285 | AccumulusFunc4: 7, 286 | AccumulusFunc5: 8, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 2, 294 | AccumulusFunc2: 5, 295 | AccumulusFunc3: 1, 296 | AccumulusFunc4: 5, 297 | AccumulusFunc5: 6, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 3, 305 | AccumulusFunc2: 1, 306 | AccumulusFunc3: 6, 307 | AccumulusFunc4: 4, 308 | AccumulusFunc5: 7, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 7, 316 | AccumulusFunc2: 2, 317 | AccumulusFunc3: 3, 318 | AccumulusFunc4: 8, 319 | AccumulusFunc5: 8, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 5, 327 | AccumulusFunc2: 0, 328 | AccumulusFunc3: 2, 329 | AccumulusFunc4: 8, 330 | AccumulusFunc5: 5, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 4, 338 | AccumulusFunc2: 3, 339 | AccumulusFunc3: 5, 340 | AccumulusFunc4: 9, 341 | AccumulusFunc5: 5, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Data/byFunc/memUsageMock.ts: -------------------------------------------------------------------------------- 1 | export const memUsageMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 66, 8 | AccumulusFunc2: 88, 9 | AccumulusFunc3: 70, 10 | AccumulusFunc4: 76, 11 | AccumulusFunc5: 66, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 15, 19 | AccumulusFunc2: 76, 20 | AccumulusFunc3: 65, 21 | AccumulusFunc4: 37, 22 | AccumulusFunc5: 75, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 60, 30 | AccumulusFunc2: 51, 31 | AccumulusFunc3: 97, 32 | AccumulusFunc4: 14, 33 | AccumulusFunc5: 87, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 57, 41 | AccumulusFunc2: 48, 42 | AccumulusFunc3: 33, 43 | AccumulusFunc4: 5, 44 | AccumulusFunc5: 56, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 30, 52 | AccumulusFunc2: 86, 53 | AccumulusFunc3: 72, 54 | AccumulusFunc4: 44, 55 | AccumulusFunc5: 95, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 33, 63 | AccumulusFunc2: 95, 64 | AccumulusFunc3: 54, 65 | AccumulusFunc4: 69, 66 | AccumulusFunc5: 6, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 46, 74 | AccumulusFunc2: 37, 75 | AccumulusFunc3: 26, 76 | AccumulusFunc4: 68, 77 | AccumulusFunc5: 97, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 64, 85 | AccumulusFunc2: 50, 86 | AccumulusFunc3: 66, 87 | AccumulusFunc4: 35, 88 | AccumulusFunc5: 12, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 56, 96 | AccumulusFunc2: 49, 97 | AccumulusFunc3: 83, 98 | AccumulusFunc4: 7, 99 | AccumulusFunc5: 0, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 59, 107 | AccumulusFunc2: 34, 108 | AccumulusFunc3: 50, 109 | AccumulusFunc4: 1, 110 | AccumulusFunc5: 7, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 66, 118 | AccumulusFunc2: 56, 119 | AccumulusFunc3: 20, 120 | AccumulusFunc4: 97, 121 | AccumulusFunc5: 73, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 37, 129 | AccumulusFunc2: 33, 130 | AccumulusFunc3: 91, 131 | AccumulusFunc4: 39, 132 | AccumulusFunc5: 39, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 81, 140 | AccumulusFunc2: 94, 141 | AccumulusFunc3: 49, 142 | AccumulusFunc4: 55, 143 | AccumulusFunc5: 3, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 2, 151 | AccumulusFunc2: 49, 152 | AccumulusFunc3: 94, 153 | AccumulusFunc4: 76, 154 | AccumulusFunc5: 10, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 14, 162 | AccumulusFunc2: 67, 163 | AccumulusFunc3: 55, 164 | AccumulusFunc4: 15, 165 | AccumulusFunc5: 0, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 54, 173 | AccumulusFunc2: 28, 174 | AccumulusFunc3: 92, 175 | AccumulusFunc4: 1, 176 | AccumulusFunc5: 77, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 95, 184 | AccumulusFunc2: 20, 185 | AccumulusFunc3: 60, 186 | AccumulusFunc4: 69, 187 | AccumulusFunc5: 67, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 12, 195 | AccumulusFunc2: 12, 196 | AccumulusFunc3: 45, 197 | AccumulusFunc4: 66, 198 | AccumulusFunc5: 43, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 31, 206 | AccumulusFunc2: 54, 207 | AccumulusFunc3: 81, 208 | AccumulusFunc4: 13, 209 | AccumulusFunc5: 94, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 30, 217 | AccumulusFunc2: 80, 218 | AccumulusFunc3: 99, 219 | AccumulusFunc4: 55, 220 | AccumulusFunc5: 36, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 79, 228 | AccumulusFunc2: 66, 229 | AccumulusFunc3: 6, 230 | AccumulusFunc4: 52, 231 | AccumulusFunc5: 81, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 61, 239 | AccumulusFunc2: 84, 240 | AccumulusFunc3: 96, 241 | AccumulusFunc4: 13, 242 | AccumulusFunc5: 50, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 1, 250 | AccumulusFunc2: 7, 251 | AccumulusFunc3: 53, 252 | AccumulusFunc4: 11, 253 | AccumulusFunc5: 89, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 80, 261 | AccumulusFunc2: 22, 262 | AccumulusFunc3: 50, 263 | AccumulusFunc4: 3, 264 | AccumulusFunc5: 81, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 12, 272 | AccumulusFunc2: 52, 273 | AccumulusFunc3: 59, 274 | AccumulusFunc4: 94, 275 | AccumulusFunc5: 33, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 27, 283 | AccumulusFunc2: 88, 284 | AccumulusFunc3: 88, 285 | AccumulusFunc4: 91, 286 | AccumulusFunc5: 41, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 86, 294 | AccumulusFunc2: 84, 295 | AccumulusFunc3: 27, 296 | AccumulusFunc4: 2, 297 | AccumulusFunc5: 77, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 70, 305 | AccumulusFunc2: 52, 306 | AccumulusFunc3: 10, 307 | AccumulusFunc4: 11, 308 | AccumulusFunc5: 75, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 1, 316 | AccumulusFunc2: 10, 317 | AccumulusFunc3: 10, 318 | AccumulusFunc4: 23, 319 | AccumulusFunc5: 56, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 18, 327 | AccumulusFunc2: 37, 328 | AccumulusFunc3: 35, 329 | AccumulusFunc4: 34, 330 | AccumulusFunc5: 82, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 55, 338 | AccumulusFunc2: 20, 339 | AccumulusFunc3: 37, 340 | AccumulusFunc4: 26, 341 | AccumulusFunc5: 27, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Data/byFunc/memUsedVsAllo.ts: -------------------------------------------------------------------------------- 1 | import { MemUsed } from '../../server/types'; 2 | 3 | export const memUsedVsAllo: MemUsed[] = [ 4 | { 5 | name: 'AccumulusFunc1', 6 | usedAccumulusFunc1: 35, 7 | alloAccumulusFunc1: 128, 8 | diffAccumulusFunc1: 95, 9 | }, 10 | { 11 | name: 'AccumulusFunc2', 12 | usedAccumulusFunc2: 192, 13 | alloAccumulusFunc2: 256, 14 | diffAccumulusFunc2: 64, 15 | }, 16 | { 17 | name: 'AccumulusFunc3', 18 | usedAccumulusFunc3: 29, 19 | alloAccumulusFunc3: 512, 20 | diffAccumulusFunc3: 483, 21 | }, 22 | { 23 | name: 'AccumulusFunc4', 24 | usedAccumulusFunc4: 346, 25 | alloAccumulusFunc4: 512, 26 | diffAccumulusFunc4: 166, 27 | }, 28 | { 29 | name: 'AccumulusFunc5', 30 | usedAccumulusFunc5: 193, 31 | alloAccumulusFunc5: 1024, 32 | diffAccumulusFunc5: 831, 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /Data/byFunc/mostErrorsMock.ts: -------------------------------------------------------------------------------- 1 | export const mostErrorsMock = [ 2 | { 3 | name: 'Login', 4 | value: 32, 5 | }, 6 | { 7 | name: 'Signup', 8 | value: 25, 9 | }, 10 | { 11 | name: 'CreateUser', 12 | value: 23, 13 | }, 14 | { 15 | name: 'GetMetrics', 16 | value: 21, 17 | }, 18 | { 19 | name: 'AnalyzeData', 20 | value: 19, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /Data/byFunc/slowestFuncsMock.ts: -------------------------------------------------------------------------------- 1 | export const slowestFuncsMock = [ 2 | { 3 | name: 'CreateUser', 4 | value: 500, 5 | }, 6 | { 7 | name: 'Login', 8 | value: 450, 9 | }, 10 | { 11 | name: 'Signup', 12 | value: 375, 13 | }, 14 | { 15 | name: 'AnalyzeData', 16 | value: 350, 17 | }, 18 | { 19 | name: 'GetMetrics', 20 | value: 300, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /Data/byFunc/throttlesMock.ts: -------------------------------------------------------------------------------- 1 | export const throttlesMock = [ 2 | { 3 | month: 'January', 4 | day: 1, 5 | hour: 0, 6 | minute: 0, 7 | AccumulusFunc1: 0, 8 | AccumulusFunc2: 0, 9 | AccumulusFunc3: 2, 10 | AccumulusFunc4: 1, 11 | AccumulusFunc5: 0, 12 | }, 13 | { 14 | month: 'January', 15 | day: 2, 16 | hour: 0, 17 | minute: 0, 18 | AccumulusFunc1: 1, 19 | AccumulusFunc2: 1, 20 | AccumulusFunc3: 0, 21 | AccumulusFunc4: 1, 22 | AccumulusFunc5: 2, 23 | }, 24 | { 25 | month: 'January', 26 | day: 3, 27 | hour: 0, 28 | minute: 0, 29 | AccumulusFunc1: 1, 30 | AccumulusFunc2: 1, 31 | AccumulusFunc3: 2, 32 | AccumulusFunc4: 2, 33 | AccumulusFunc5: 0, 34 | }, 35 | { 36 | month: 'January', 37 | day: 4, 38 | hour: 0, 39 | minute: 0, 40 | AccumulusFunc1: 2, 41 | AccumulusFunc2: 0, 42 | AccumulusFunc3: 1, 43 | AccumulusFunc4: 0, 44 | AccumulusFunc5: 1, 45 | }, 46 | { 47 | month: 'January', 48 | day: 5, 49 | hour: 0, 50 | minute: 0, 51 | AccumulusFunc1: 1, 52 | AccumulusFunc2: 1, 53 | AccumulusFunc3: 0, 54 | AccumulusFunc4: 1, 55 | AccumulusFunc5: 0, 56 | }, 57 | { 58 | month: 'January', 59 | day: 6, 60 | hour: 0, 61 | minute: 0, 62 | AccumulusFunc1: 1, 63 | AccumulusFunc2: 1, 64 | AccumulusFunc3: 0, 65 | AccumulusFunc4: 1, 66 | AccumulusFunc5: 0, 67 | }, 68 | { 69 | month: 'January', 70 | day: 7, 71 | hour: 0, 72 | minute: 0, 73 | AccumulusFunc1: 0, 74 | AccumulusFunc2: 0, 75 | AccumulusFunc3: 0, 76 | AccumulusFunc4: 2, 77 | AccumulusFunc5: 2, 78 | }, 79 | { 80 | month: 'January', 81 | day: 8, 82 | hour: 0, 83 | minute: 0, 84 | AccumulusFunc1: 0, 85 | AccumulusFunc2: 1, 86 | AccumulusFunc3: 0, 87 | AccumulusFunc4: 0, 88 | AccumulusFunc5: 2, 89 | }, 90 | { 91 | month: 'January', 92 | day: 9, 93 | hour: 0, 94 | minute: 0, 95 | AccumulusFunc1: 1, 96 | AccumulusFunc2: 0, 97 | AccumulusFunc3: 0, 98 | AccumulusFunc4: 0, 99 | AccumulusFunc5: 2, 100 | }, 101 | { 102 | month: 'January', 103 | day: 10, 104 | hour: 0, 105 | minute: 0, 106 | AccumulusFunc1: 1, 107 | AccumulusFunc2: 1, 108 | AccumulusFunc3: 2, 109 | AccumulusFunc4: 1, 110 | AccumulusFunc5: 1, 111 | }, 112 | { 113 | month: 'January', 114 | day: 11, 115 | hour: 0, 116 | minute: 0, 117 | AccumulusFunc1: 0, 118 | AccumulusFunc2: 1, 119 | AccumulusFunc3: 1, 120 | AccumulusFunc4: 2, 121 | AccumulusFunc5: 0, 122 | }, 123 | { 124 | month: 'January', 125 | day: 12, 126 | hour: 0, 127 | minute: 0, 128 | AccumulusFunc1: 2, 129 | AccumulusFunc2: 0, 130 | AccumulusFunc3: 2, 131 | AccumulusFunc4: 0, 132 | AccumulusFunc5: 0, 133 | }, 134 | { 135 | month: 'January', 136 | day: 13, 137 | hour: 0, 138 | minute: 0, 139 | AccumulusFunc1: 1, 140 | AccumulusFunc2: 1, 141 | AccumulusFunc3: 2, 142 | AccumulusFunc4: 2, 143 | AccumulusFunc5: 2, 144 | }, 145 | { 146 | month: 'January', 147 | day: 14, 148 | hour: 0, 149 | minute: 0, 150 | AccumulusFunc1: 2, 151 | AccumulusFunc2: 1, 152 | AccumulusFunc3: 2, 153 | AccumulusFunc4: 0, 154 | AccumulusFunc5: 2, 155 | }, 156 | { 157 | month: 'January', 158 | day: 15, 159 | hour: 0, 160 | minute: 0, 161 | AccumulusFunc1: 2, 162 | AccumulusFunc2: 1, 163 | AccumulusFunc3: 0, 164 | AccumulusFunc4: 1, 165 | AccumulusFunc5: 1, 166 | }, 167 | { 168 | month: 'January', 169 | day: 16, 170 | hour: 0, 171 | minute: 0, 172 | AccumulusFunc1: 2, 173 | AccumulusFunc2: 0, 174 | AccumulusFunc3: 0, 175 | AccumulusFunc4: 1, 176 | AccumulusFunc5: 1, 177 | }, 178 | { 179 | month: 'January', 180 | day: 17, 181 | hour: 0, 182 | minute: 0, 183 | AccumulusFunc1: 1, 184 | AccumulusFunc2: 0, 185 | AccumulusFunc3: 0, 186 | AccumulusFunc4: 1, 187 | AccumulusFunc5: 2, 188 | }, 189 | { 190 | month: 'January', 191 | day: 18, 192 | hour: 0, 193 | minute: 0, 194 | AccumulusFunc1: 0, 195 | AccumulusFunc2: 1, 196 | AccumulusFunc3: 1, 197 | AccumulusFunc4: 2, 198 | AccumulusFunc5: 0, 199 | }, 200 | { 201 | month: 'January', 202 | day: 19, 203 | hour: 0, 204 | minute: 0, 205 | AccumulusFunc1: 1, 206 | AccumulusFunc2: 2, 207 | AccumulusFunc3: 0, 208 | AccumulusFunc4: 2, 209 | AccumulusFunc5: 0, 210 | }, 211 | { 212 | month: 'January', 213 | day: 20, 214 | hour: 0, 215 | minute: 0, 216 | AccumulusFunc1: 1, 217 | AccumulusFunc2: 2, 218 | AccumulusFunc3: 2, 219 | AccumulusFunc4: 1, 220 | AccumulusFunc5: 2, 221 | }, 222 | { 223 | month: 'January', 224 | day: 21, 225 | hour: 0, 226 | minute: 0, 227 | AccumulusFunc1: 0, 228 | AccumulusFunc2: 1, 229 | AccumulusFunc3: 1, 230 | AccumulusFunc4: 0, 231 | AccumulusFunc5: 2, 232 | }, 233 | { 234 | month: 'January', 235 | day: 22, 236 | hour: 0, 237 | minute: 0, 238 | AccumulusFunc1: 0, 239 | AccumulusFunc2: 1, 240 | AccumulusFunc3: 0, 241 | AccumulusFunc4: 2, 242 | AccumulusFunc5: 2, 243 | }, 244 | { 245 | month: 'January', 246 | day: 23, 247 | hour: 0, 248 | minute: 0, 249 | AccumulusFunc1: 1, 250 | AccumulusFunc2: 0, 251 | AccumulusFunc3: 1, 252 | AccumulusFunc4: 2, 253 | AccumulusFunc5: 0, 254 | }, 255 | { 256 | month: 'January', 257 | day: 24, 258 | hour: 0, 259 | minute: 0, 260 | AccumulusFunc1: 0, 261 | AccumulusFunc2: 2, 262 | AccumulusFunc3: 1, 263 | AccumulusFunc4: 2, 264 | AccumulusFunc5: 0, 265 | }, 266 | { 267 | month: 'January', 268 | day: 25, 269 | hour: 0, 270 | minute: 0, 271 | AccumulusFunc1: 2, 272 | AccumulusFunc2: 0, 273 | AccumulusFunc3: 0, 274 | AccumulusFunc4: 1, 275 | AccumulusFunc5: 2, 276 | }, 277 | { 278 | month: 'January', 279 | day: 26, 280 | hour: 0, 281 | minute: 0, 282 | AccumulusFunc1: 1, 283 | AccumulusFunc2: 2, 284 | AccumulusFunc3: 0, 285 | AccumulusFunc4: 1, 286 | AccumulusFunc5: 0, 287 | }, 288 | { 289 | month: 'January', 290 | day: 27, 291 | hour: 0, 292 | minute: 0, 293 | AccumulusFunc1: 2, 294 | AccumulusFunc2: 0, 295 | AccumulusFunc3: 1, 296 | AccumulusFunc4: 2, 297 | AccumulusFunc5: 1, 298 | }, 299 | { 300 | month: 'January', 301 | day: 28, 302 | hour: 0, 303 | minute: 0, 304 | AccumulusFunc1: 0, 305 | AccumulusFunc2: 2, 306 | AccumulusFunc3: 2, 307 | AccumulusFunc4: 2, 308 | AccumulusFunc5: 1, 309 | }, 310 | { 311 | month: 'January', 312 | day: 29, 313 | hour: 0, 314 | minute: 0, 315 | AccumulusFunc1: 1, 316 | AccumulusFunc2: 1, 317 | AccumulusFunc3: 1, 318 | AccumulusFunc4: 0, 319 | AccumulusFunc5: 0, 320 | }, 321 | { 322 | month: 'January', 323 | day: 30, 324 | hour: 0, 325 | minute: 0, 326 | AccumulusFunc1: 1, 327 | AccumulusFunc2: 2, 328 | AccumulusFunc3: 2, 329 | AccumulusFunc4: 1, 330 | AccumulusFunc5: 0, 331 | }, 332 | { 333 | month: 'January', 334 | day: 31, 335 | hour: 0, 336 | minute: 0, 337 | AccumulusFunc1: 1, 338 | AccumulusFunc2: 1, 339 | AccumulusFunc3: 0, 340 | AccumulusFunc4: 2, 341 | AccumulusFunc5: 2, 342 | }, 343 | ]; 344 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13 2 | WORKDIR /usr/src/app 3 | COPY . /usr/src/app 4 | RUN npm install -g typescript 5 | RUN npm install 6 | RUN npm run compile-prod 7 | RUN npm run build-dev 8 | EXPOSE 3000 9 | CMD ["npm", "run", "accumulus"] 10 | # "accumulus": "NODE_ENV=production node --es-module-specifier-resolution=node ./dist/server/server.js" 11 | -------------------------------------------------------------------------------- /Dockerrun.aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSEBDockerrunVersion": "1", 3 | "Image": { 4 | "Name": "070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-deploy:", 5 | "Update": "true" 6 | }, 7 | "Ports": [{ 8 | "ContainerPort": "3000" 9 | }] 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2022 Accumulus 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | Accumulus 4 |
5 | 6 |

About Accumulus

7 | 8 |

9 | AWS Lambda functions are the central computing components of AWS serverless application architecture. 10 | It is intimidating to navigate the extensive AWS docs and hundreds of services, and can be challenging to quickly visualize metrics for AWS Lambda functions.

11 | 12 |

Currently, monitoring tools for Lambda make it difficult to directly compare two Lambda functions. Our solution is a web application that provides comprehensive charts for direct comparison of functions while presenting bottlenecks and potential areas for resource allocation adjustments.

13 | 14 | Project Links: [Github](https://github.com/oslabs-beta/Accumulus) | [Linkedin](https://www.linkedin.com/addThisSoon) | [Press](https://medium.com/@ericzfwu/simplify-your-aws-lambda-view-7ebb7dbaac4b) 15 | 16 |

🛠️ Getting Started:

17 | 18 |

Visit www.accumulus.dev

19 | 20 |

1. Existing user? You can log in using your email and password!

21 | 22 |

2. For new users, click "Register" to create a new user account.

23 | 24 |

3. Follow the link and instructions on the Registration page to assign a Cloud Formation Stack.

25 | 26 |

4. Copy and paste the unique ARN outputted from the prior step.

27 | 28 |

5. Register and you're done!

29 | 30 |

Monitoring Features:

31 | 32 | 1. At the Dashboard, users can select different time increments for a broad overview of their Lambda funtions, as well as select from different AWS regions. 33 | 34 |

35 | 36 |

37 | 38 | 2. Users can also refresh the page with the sync button to grab the most recent data. 39 | 40 |

41 | 42 |

43 | 44 | 3. To get a more in-depth view of one or many functions, navigate to the Functions page. Once on the Functions Page, users can select the functions that they would like to view, as well as changing the time increments displayed on the graphs. 45 | 46 |

47 | 48 |

49 | 50 | 4. On the Memory Page, users can select the functions that they would like to view, and are presented with information for memory allocation adjustments. Functions that are over-allocated with over 80% free space are listed. These are functions that may be eligible for a tune up in allocation settings. 51 | 52 |

53 | 54 |

55 | 56 |

Authors:

57 | 58 | Matt Femia - [Github](https://github.com/mattfemia) || [Linkedin](https://www.linkedin.com/in/mattfemia/) 59 | 60 | Christian Sansovich - [Github](https://github.com/christiansansovich) || [Linkedin](https://www.linkedin.com/in/christian-sansovich/) 61 | 62 | Dan Teng - [Github](https://github.com/danwteng) || [Linkedin](https://www.linkedin.com/in/danwteng/) 63 | 64 | Eric Wu - [Github](https://github.com/EZW1) || [Linkedin](https://www.linkedin.com/in/ericzfwu/) 65 | 66 | Mark Yencheske - [Github](https://github.com/markyencheske) || [Linkedin](https://www.linkedin.com/in/mark-yencheske-62698122b/) 67 | 68 | Project Links: [Github](https://github.com/oslabs-beta/Accumulus) || [Linkedin](https://www.linkedin.com/company/accumulusdev/) || [Medium](https://medium.com/@ericzfwu/simplify-your-aws-lambda-view-7ebb7dbaac4b) 69 | 70 |

Contributing:

71 | 72 |

Have a suggestion? Found a bug? Want to make Accumulus better?

73 |

Please submit issues/pull requests if you have feedback or message the Accumuls team to be added as a contributor: accmulusdev@gmail.com

74 | 75 |

💻 Built with:

76 | 77 | - [Typescript](https://www.typescriptlang.org/) 78 | - [React](https://reactjs.org/) 79 | - [ReactRouter](https://reactrouter.com/) 80 | - [NodeJS](https://nodejs.org/en/) 81 | - [Express](https://expressjs.com/) 82 | - [MongoDB](https://www.mongodb.com/) 83 | - [Mongoose](https://mongoosejs.com/) 84 | - [Styled Components](https://styled-components.com) 85 | - [ReCharts](https://recharts.org/) 86 | - [Redis](https://redis.io) 87 | 88 |

License

89 | 90 | This project is licensed under the MIT License - see the LICENSE.md file for details 91 | -------------------------------------------------------------------------------- /__tests__/backend/server.test.js: -------------------------------------------------------------------------------- 1 | // import 'regenerator-runtime/runtime'; 2 | 3 | const request = require('supertest'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | const server = 'http://localhost:3000' 8 | 9 | describe('Route integration', () => { 10 | describe('/api/user/login', () => { 11 | describe('GET', () => { 12 | xit ('responds with 200 status and json content type', async () => { 13 | return request(server) 14 | .get('/api/user/login') 15 | .expect('Content-Type', /json/) 16 | .expect(200) 17 | }) 18 | }) 19 | }) 20 | }) -------------------------------------------------------------------------------- /__tests__/supertest.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | import app from '../server/server'; 3 | const server = 'http://localhost:3000'; 4 | 5 | const mockResponse = () => { 6 | const res = { 7 | status: () => res, 8 | json: () => res 9 | }; 10 | return res; 11 | }; 12 | 13 | describe('Router Testing Suite', () => { 14 | 15 | }) 16 | describe('Home Route Integration Test', () => { 17 | describe('/api/aws/', () => { 18 | describe('GET', () => { 19 | it('responds with 200 status and application/json content type', async () => { 20 | return request(server) 21 | .get('/api/aws/') 22 | .expect('Content-Type', /json/) 23 | .expect(200) 24 | }); 25 | }); 26 | }); 27 | }) 28 | 29 | -------------------------------------------------------------------------------- /containers/app.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13 2 | WORKDIR /usr/src/app 3 | COPY . /usr/src/app 4 | RUN npm install 5 | RUN ["npm", "run", "compile-prod"] 6 | RUN ["npm", "run", "build"] 7 | COPY dist /usr/src/app 8 | EXPOSE 3000 9 | CMD ["node", "./dist/server/server.js"] -------------------------------------------------------------------------------- /containers/eb.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13 2 | RUN npm install -g webpack 3 | WORKDIR /usr/src/app 4 | COPY . /usr/src/app/ 5 | RUN npm install 6 | RUN ["npm", "run", "compile-prod"] 7 | RUN ["npm", "run", "build-dev"] 8 | COPY dist /usr/src/app/ 9 | EXPOSE 8080 -------------------------------------------------------------------------------- /containers/tsbuild.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13 2 | WORKDIR /usr/src/app 3 | COPY . /usr/src/app 4 | RUN npm install 5 | RUN ["npm", "run", "compile-prod"] 6 | RUN ["npm", "run", "build-dev"] 7 | EXPOSE 8080 8 | EXPOSE 3000 9 | CMD ["npm", "run", "dev:hot"] 10 | # "NODE_ENV=development nodemon --es-module-specifier-resolution=node 11 | # ./dist/server/server.js & webpack-dev-server --open --mode development" -------------------------------------------------------------------------------- /containers/webpack.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13 2 | WORKDIR /usr/src/app/dist/ 3 | COPY . /usr/src/app/ 4 | RUN npm install 5 | RUN ["npm", "run", "compile-prod"] 6 | RUN ["npm", "run", "build-dev"] 7 | COPY dist /usr/src/app/ 8 | EXPOSE 8080 9 | CMD ["npm", "run", "dev:hot"] -------------------------------------------------------------------------------- /context/@types/data.d.ts: -------------------------------------------------------------------------------- 1 | 2 | //type definitions for DataContext - not being used 3 | 4 | 5 | export type DataContextState = { 6 | funcNames?: string[]; 7 | start?: boolean; 8 | userRegion?: string; 9 | totalInvocations?: object[]; 10 | totalErrors?: object[]; 11 | totalCost?: object[]; 12 | slowestFuncs?: object[], 13 | mostErroredFuncs?: object[], 14 | errorMsgs?: object[], 15 | memUsedVsAllo?: object[], 16 | invocations?: object[], 17 | duration?: object[], 18 | errors?: object[], 19 | memUsage?: object[], 20 | cost?: object[], 21 | throttles?: object[], 22 | timePeriod?: string, 23 | syncData?: boolean, 24 | currentView?: string, 25 | 26 | metricType?: string, 27 | dataSum?: string, 28 | 29 | //user state 30 | nameReg?: string, 31 | emailReg?: string, 32 | passwordReg?: string, 33 | arnReg?: string, 34 | //userRegion is above 35 | EXTERNAL_ID?: string, 36 | email?: string, 37 | password?: string, 38 | 39 | //testing global name 40 | globalName?: string, 41 | setGlobalName?: function, 42 | 43 | 44 | } 45 | 46 | export type DataProviderProps ={ 47 | children: React.ReactNode 48 | } 49 | 50 | 51 | 52 | 53 | // // interface IData { 54 | // // functionNames?: string[]; 55 | // // stuff?: { 56 | // // invocations?: { 57 | // // month: string; 58 | // // day: number; 59 | // // name1: string; 60 | // // }[]; 61 | // // }; 62 | // // } 63 | 64 | // interface IDataContext { 65 | // // data?: IData; 66 | // data: string; 67 | // receivedData?: (arg: string) => void; 68 | // } 69 | 70 | // const defaultState: IDataContext = { 71 | // // data: { 72 | // // functionNames: ['name1', 'name2', 'name3', 'name4', 'name5'], 73 | // // stuff: { 74 | // // invocations: [ 75 | // // { 76 | // // month: 'Jan', 77 | // // day: 0, 78 | // // name1: 'testName', 79 | // // }, 80 | // // ], 81 | // // } 82 | // // } 83 | // data: 'Hello', 84 | // }; -------------------------------------------------------------------------------- /context/AppReducer.tsx: -------------------------------------------------------------------------------- 1 | 2 | //AppReducer is not being used 3 | 4 | export const AppReducer = (state: any, action: any) => { 5 | console.log('entered AppReducer') 6 | switch(action.type){ 7 | case 'CHANGE_VIEW': 8 | return { 9 | ...state, 10 | testView: action.payload 11 | } 12 | default: 13 | return state; 14 | } 15 | } -------------------------------------------------------------------------------- /context/dataContext.tsx: -------------------------------------------------------------------------------- 1 | // dataContext is not being used, but is here for a future iteration 2 | 3 | import React, { useMemo, useState, FC, createContext, useContext, useReducer } from 'react'; 4 | import { DataContextState, DataProviderProps } from './@types/data'; 5 | import { AppReducer } from './AppReducer'; 6 | 7 | const defaultState = { 8 | funcNames: ['func1', 'func2', 'func3'], 9 | start: false, 10 | userRegion: 'us-east-2', 11 | //dashboard- all funcs hooks 12 | totalInvocations: [], 13 | totalErrors: [], 14 | totalCost: [], 15 | //dashboard - by func hooks 16 | slowestFuncs: [], 17 | mostErroredFuncs: [], 18 | errorMsgs: [], 19 | memUsedVsAllo: [], 20 | //functions 21 | invocations: [], 22 | duration: [], 23 | errors: [], 24 | memUsage: [], 25 | cost: [], 26 | throttles: [], 27 | timePeriod: '7d', 28 | syncData: false, 29 | currentView: 'splash', 30 | metricType: 'Invocations', 31 | dataSum: 'Sum', 32 | //user state 33 | nameReg: '', 34 | emailReg: '', 35 | passwordReg: '', 36 | arnReg: '', 37 | //userRegion is above 38 | EXTERNAL_ID: '', 39 | email: '', 40 | password: '', 41 | //testing global name 42 | globalName: '', 43 | setGlobalName: ()=>{}, 44 | } 45 | 46 | export const DataContext = createContext(defaultState); 47 | 48 | // export const DataProvider: FC<{children: any}> = ({ children }) => { 49 | // const [state, dispatch] = useReducer(AppReducer, defaultState); 50 | 51 | // //add actions here? 52 | // // function changeView(view: string){ 53 | // // console.log('entered changeView') 54 | // // dispatch({ 55 | // // type: 'CHANGE_VIEW', 56 | // // payload: view 57 | // // }) 58 | // // } 59 | 60 | // const [globalName, setGlobalName ] = useState('mark test'); 61 | 62 | // const val:any = useMemo( 63 | // ()=>({globalName, setGlobalName}), 64 | // [globalName] 65 | // ) 66 | 67 | // return ( 68 | // 73 | 74 | // { children } 75 | // 76 | // ); 77 | // }; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | //------------GRAVEYARD------ 90 | 91 | 92 | // // interface IData { 93 | // // functionNames?: string[]; 94 | // // stuff?: { 95 | // // invocations?: { 96 | // // month: string; 97 | // // day: number; 98 | // // name1: string; 99 | // // }[]; 100 | // // }; 101 | // // } 102 | 103 | // interface IDataContext { 104 | // // data?: IData; 105 | // data: string; 106 | // receivedData?: (arg: string) => void; 107 | // } 108 | 109 | // const defaultState: IDataContext = { 110 | // // data: { 111 | // // functionNames: ['name1', 'name2', 'name3', 'name4', 'name5'], 112 | // // stuff: { 113 | // // invocations: [ 114 | // // { 115 | // // month: 'Jan', 116 | // // day: 0, 117 | // // name1: 'testName', 118 | // // }, 119 | // // ], 120 | // // } 121 | // // } 122 | // data: 'Hello', 123 | // }; 124 | 125 | 126 | 127 | // interface Props { 128 | // children: React.ReactNode; 129 | // } 130 | 131 | // export const AppWrapper = ({ children }: Props) => { 132 | // const [data, setData] = useState(defaultState.data); 133 | 134 | // //Do we need to put in parameter so when receivedData is called, 135 | // //it puts in the fetch request data and set that to data state? 136 | // const receivedData = (arg: string) => { 137 | // setData(arg); 138 | // console.log(`Data inside setData: ${data}`); 139 | // }; 140 | 141 | // return ( 142 | // <> 143 | // 144 | // {children} 145 | // 146 | // 147 | // ); 148 | // }; 149 | 150 | // export function useDataContext() { 151 | // return React.useContext(DataContext); 152 | // } 153 | 154 | // // Landing Page 155 | // // -Nav Menu 156 | // // -Registration Page 157 | // // - Log In 158 | // // -Dashboard —-GET request for user data here? 159 | // // -Dash Nav Menu 160 | // // -Functions Page 161 | // // -Step Functions Page 162 | // // -Cost Analysis Page 163 | 164 | // // Matts Data Baby 165 | // // { 166 | // // functionNames: [name1, name2, …], 167 | // // data: { 168 | // // invocations: [{ 169 | // // ‘month’: ‘Jan’, 170 | // // ‘day’: 0, … , 171 | // // name1: value, 172 | // // name2: value 173 | // // }], 174 | // // duration: { ... } 175 | // // } 176 | // // } 177 | -------------------------------------------------------------------------------- /context/userContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, FC } from 'react'; 2 | 3 | interface IUserContext { 4 | name?: string; 5 | setName?: () => void; 6 | storeName: Function; 7 | email?: string; 8 | setEmail?: () => void; 9 | storeEmail: Function; 10 | } 11 | 12 | const defaultUser = { 13 | name: 'Back!', 14 | storeName: () => {}, 15 | email: 'defaultEmail', 16 | storeEmail: () => {}, 17 | }; 18 | 19 | export const UserContext = React.createContext(defaultUser); 20 | 21 | interface Props { 22 | children: React.ReactNode; 23 | } 24 | 25 | export const UserProvider = ({ children }: Props) => { 26 | const [name, setName] = useState(defaultUser.name); 27 | const [email, setEmail] = useState(defaultUser.email); 28 | 29 | // const confirmedUser = (arg: any): any => setUser(user); 30 | const storeName = (val: string) => { 31 | setName(val); 32 | }; 33 | const storeEmail = (val: string) => { 34 | setEmail(val); 35 | }; 36 | 37 | return ( 38 | <> 39 | 40 | {children} 41 | 42 | 43 | ); 44 | }; 45 | 46 | export function useUserContext() { 47 | return React.useContext(UserContext); 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /docker-compose-dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | dev: 4 | image: "070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-deploy:latest" 5 | container_name: "accumulus-production" 6 | ports: 7 | - "8080:8080" 8 | volumes: 9 | - .:/usr/src/app 10 | - node_modules:/usr/src/app/node_modules 11 | command: npm run dev:hot 12 | volumes: 13 | node_modules: {} -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | test: 4 | image: "070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-deploy:latest" 5 | container_name: "accumulus-test" 6 | ports: 7 | - "3000:3000" 8 | volumes: 9 | - .:/usr/src/app 10 | - node_modules:/usr/src/app/node_modules 11 | command: npm run test 12 | volumes: 13 | node_modules: {} 14 | test-db-volume: {} -------------------------------------------------------------------------------- /jest-teardown.js: -------------------------------------------------------------------------------- 1 | import { testServer } from './jest.setup' 2 | 3 | export const closingTestServer = async (globalConfig) => { 4 | testServer.close() 5 | } -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property and type check, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | import type { Config } from "@jest/types" 7 | 8 | const config: Config.InitialOptions = { 9 | preset: "ts-jest", 10 | testEnvironment: "node", 11 | verbose: true, 12 | // automock: true, 13 | } 14 | export default config; 15 | 16 | // **** export default { 17 | // All imported modules in your tests should be mocked automatically 18 | // automock: false, 19 | 20 | // Stop running tests after `n` failures 21 | // bail: 0, 22 | 23 | // The directory where Jest should store its cached dependency information 24 | // cacheDirectory: "/private/var/folders/sx/wrgqkkvn3lscsqq05000r43w0000gn/T/jest_dx", 25 | 26 | // Automatically clear mock calls, instances and results before every test 27 | //**** */ clearMocks: true, 28 | 29 | // Indicates whether the coverage information should be collected while executing the test 30 | // collectCoverage: false, 31 | 32 | // An array of glob patterns indicating a set of files for which coverage information should be collected 33 | // collectCoverageFrom: undefined, 34 | 35 | // The directory where Jest should output its coverage files 36 | // coverageDirectory: undefined, 37 | 38 | // An array of regexp pattern strings used to skip coverage collection 39 | // coveragePathIgnorePatterns: [ 40 | // "/node_modules/" 41 | // ], 42 | 43 | // Indicates which provider should be used to instrument code for coverage 44 | // *** coverageProvider: "v8", 45 | 46 | // A list of reporter names that Jest uses when writing coverage reports 47 | // coverageReporters: [ 48 | // "json", 49 | // "text", 50 | // "lcov", 51 | // "clover" 52 | // ], 53 | 54 | // An object that configures minimum threshold enforcement for coverage results 55 | // coverageThreshold: undefined, 56 | 57 | // A path to a custom dependency extractor 58 | // dependencyExtractor: undefined, 59 | 60 | // Make calling deprecated APIs throw helpful error messages 61 | // errorOnDeprecated: false, 62 | 63 | // Force coverage collection from ignored files using an array of glob patterns 64 | // forceCoverageMatch: [], 65 | 66 | // A path to a module which exports an async function that is triggered once before all test suites 67 | // globalSetup: undefined, 68 | 69 | // A path to a module which exports an async function that is triggered once after all test suites 70 | // globalTeardown: undefined, 71 | 72 | // A set of global variables that need to be available in all test environments 73 | // globals: {}, 74 | 75 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 76 | // maxWorkers: "50%", 77 | 78 | // An array of directory names to be searched recursively up from the requiring module's location 79 | // moduleDirectories: [ 80 | // "node_modules" 81 | // ], 82 | 83 | // An array of file extensions your modules use 84 | // moduleFileExtensions: [ 85 | // "js", 86 | // "jsx", 87 | // "ts", 88 | // "tsx", 89 | // "json", 90 | // "node" 91 | // ], 92 | 93 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 94 | // moduleNameMapper: {}, 95 | 96 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 97 | // modulePathIgnorePatterns: [], 98 | 99 | // Activates notifications for test results 100 | // notify: false, 101 | 102 | // An enum that specifies notification mode. Requires { notify: true } 103 | // notifyMode: "failure-change", 104 | 105 | // A preset that is used as a base for Jest's configuration 106 | // preset: undefined, 107 | 108 | // Run tests from one or more projects 109 | // projects: undefined, 110 | 111 | // Use this configuration option to add custom reporters to Jest 112 | // reporters: undefined, 113 | 114 | // Automatically reset mock state before every test 115 | // resetMocks: false, 116 | 117 | // Reset the module registry before running each individual test 118 | // resetModules: false, 119 | 120 | // A path to a custom resolver 121 | // resolver: undefined, 122 | 123 | // Automatically restore mock state and implementation before every test 124 | // restoreMocks: false, 125 | 126 | // The root directory that Jest should scan for tests and modules within 127 | // rootDir: undefined, 128 | 129 | // A list of paths to directories that Jest should use to search for files in 130 | // roots: [ 131 | // "" 132 | // ], 133 | 134 | // Allows you to use a custom runner instead of Jest's default test runner 135 | // runner: "jest-runner", 136 | 137 | // The paths to modules that run some code to configure or set up the testing environment before each test 138 | // setupFiles: [], 139 | 140 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 141 | // setupFilesAfterEnv: [], 142 | 143 | // The number of seconds after which a test is considered as slow and reported as such in the results. 144 | // slowTestThreshold: 5, 145 | 146 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 147 | // snapshotSerializers: [], 148 | 149 | // The test environment that will be used for testing 150 | // testEnvironment: "jest-environment-node", 151 | 152 | // Options that will be passed to the testEnvironment 153 | // testEnvironmentOptions: {}, 154 | 155 | // Adds a location field to test results 156 | // testLocationInResults: false, 157 | 158 | // The glob patterns Jest uses to detect test files 159 | // testMatch: [ 160 | // "**/__tests__/**/*.[jt]s?(x)", 161 | // "**/?(*.)+(spec|test).[tj]s?(x)" 162 | // ], 163 | 164 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 165 | // testPathIgnorePatterns: [ 166 | // "/node_modules/" 167 | // ], 168 | 169 | // The regexp pattern or array of patterns that Jest uses to detect test files 170 | // testRegex: [], 171 | 172 | // This option allows the use of a custom results processor 173 | // testResultsProcessor: undefined, 174 | 175 | // This option allows use of a custom test runner 176 | // testRunner: "jest-circus/runner", 177 | 178 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 179 | // testURL: "http://localhost", 180 | 181 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 182 | // timers: "real", 183 | 184 | // A map from regular expressions to paths to transformers 185 | // transform: undefined, 186 | 187 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 188 | // transformIgnorePatterns: [ 189 | // "/node_modules/", 190 | // "\\.pnp\\.[^\\/]+$" 191 | // ], 192 | 193 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 194 | // unmockedModulePathPatterns: undefined, 195 | 196 | // Indicates whether each individual test should be reported during the run 197 | // verbose: undefined, 198 | 199 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 200 | // watchPathIgnorePatterns: [], 201 | 202 | // Whether to use watchman for file crawling 203 | // watchman: true, 204 | // }; 205 | -------------------------------------------------------------------------------- /jest.setup.ts: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from 'regenerator-runtime'; 2 | 3 | module.exports = () => { 4 | global.testServer = require('./server'); 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "accumulus", 3 | "version": "1.0.0", 4 | "description": "A visualization and optimization insight tool for AWS Lambda", 5 | "main": "/src/index.js/", 6 | "type": "module", 7 | "author": "Eric Wu, Matt Femia, Dan Teng, Christian Sansovich, Mark Yencheske", 8 | "scripts": { 9 | "compile-dev": "npx tsc -w", 10 | "compile-prod": "tsc", 11 | "build-dev": "webpack --mode development", 12 | "build-prod": "webpack --mode production", 13 | "start": "webpack serve nodemon ./dist/server/server.js", 14 | "dev": "NODE_ENV=development webpack-dev-server --open & nodemon --es-module-specifier-resolution=node dist/server/server.js", 15 | "test": "jest", 16 | "build": "tsc -p tsconfig.build.json", 17 | "push-docker-webpack": "aws ecr get-login-password --region us-east-2 --profile accumulus | docker login --username AWS --password-stdin 070809985305.dkr.ecr.us-east-2.amazonaws.com & docker build -f webpack.Dockerfile -t accumulus-webpack . & docker tag accumulus-frontend:latest 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-frontend:latest & docker push 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-webpack:latest", 18 | "push-docker-app": "aws ecr get-login-password --region us-east-2 --profile accumulus | docker login --username AWS --password-stdin 070809985305.dkr.ecr.us-east-2.amazonaws.com & docker build -f app.Dockerfile -t accumulus-app . & docker tag accumulus-backend:latest 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-frontend:latest & docker push 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-app:latest", 19 | "production": "NODE_ENV=development nodemon ./server/server.js & webpack-dev-server --open --hot", 20 | "dev:hot": "NODE_ENV=development nodemon --es-module-specifier-resolution=node ./dist/server/server.js & webpack-dev-server --open --mode development", 21 | "docker-dev:hot": "docker-compose -f docker-compose-dev.yml up", 22 | "accumulus": "NODE_ENV=production node --es-module-specifier-resolution=node ./dist/server/server.js" 23 | }, 24 | "keywords": [], 25 | "license": "ISC", 26 | "resolutions": { 27 | "styled-components": "^5" 28 | }, 29 | "babel": { 30 | "plugins": [ 31 | "babel-plugin-styled-components" 32 | ] 33 | }, 34 | "dependencies": { 35 | "@aws-sdk/client-cloudwatch": "^3.78.0", 36 | "@aws-sdk/client-cloudwatch-logs": "^3.81.0", 37 | "@aws-sdk/client-lambda": "^3.78.0", 38 | "@aws-sdk/client-sts": "^3.78.0", 39 | "@fortawesome/fontawesome-svg-core": "^6.1.1", 40 | "@fortawesome/free-solid-svg-icons": "^6.1.1", 41 | "@fortawesome/react-fontawesome": "^0.1.18", 42 | "@react-three/drei": "^9.11.0", 43 | "@react-three/fiber": "^8.0.17", 44 | "@tidyjs/tidy": "^2.4.5", 45 | "@types/react": "^18.0.9", 46 | "acorn": "^8.7.1", 47 | "aws-sdk": "^2.1122.0", 48 | "bcrypt": "^5.0.1", 49 | "bcryptjs": "^2.4.3", 50 | "cookie-parser": "^1.4.6", 51 | "cors": "^2.8.5", 52 | "dotenv": "^16.0.0", 53 | "express": "^4.17.3", 54 | "ioredis": "^5.0.4", 55 | "lamina": "^1.1.19", 56 | "moment": "^2.29.3", 57 | "mongoose": "^6.3.1", 58 | "perlin": "^1.0.0", 59 | "postcss": "^8.4.12", 60 | "react": "^18.0.0", 61 | "react-dom": "^18.0.0", 62 | "react-hook-form": "^7.30.0", 63 | "react-router-dom": "^5.3.0", 64 | "recharts": "^2.1.9", 65 | "sass": "^1.51.0", 66 | "styled-components": "^5.3.5", 67 | "supertest": "^6.2.3", 68 | "three": "^0.140.2", 69 | "three-custom-shader-material": "^3.3.6", 70 | "three-noise": "^1.1.2", 71 | "uuid": "^8.3.2", 72 | "webpack": "^5.72.0", 73 | "webpack-hot-middleware": "^2.25.1" 74 | }, 75 | "devDependencies": { 76 | "@babel/core": "^7.17.12", 77 | "@babel/preset-env": "^7.16.11", 78 | "@babel/preset-react": "^7.16.7", 79 | "@babel/preset-typescript": "^7.16.7", 80 | "@types/bcryptjs": "^2.4.2", 81 | "@types/cookie-parser": "^1.4.3", 82 | "@types/cors": "^2.8.12", 83 | "@types/express": "^4.17.13", 84 | "@types/jest": "^27.5.1", 85 | "@types/mongoose": "^5.11.97", 86 | "@types/node": "^17.0.31", 87 | "@types/react-dom": "^18.0.0", 88 | "@types/react-router-dom": "^5.3.3", 89 | "@types/recharts": "^1.8.23", 90 | "@types/regenerator-runtime": "^0.13.1", 91 | "@types/styled-components": "^5.1.25", 92 | "@types/supertest": "^2.0.12", 93 | "@types/three": "^0.140.0", 94 | "@types/uuid": "^8.3.4", 95 | "babel-loader": "^8.2.5", 96 | "babel-plugin-styled-components": "^2.0.7", 97 | "babelify": "^10.0.0", 98 | "css-loader": "^6.7.1", 99 | "eslint": "8.14.0", 100 | "eslint-config-next": "12.1.5", 101 | "html-webpack-plugin": "^5.5.0", 102 | "jest": "^28.1.0", 103 | "nodemon": "^2.0.15", 104 | "sass-loader": "^12.6.0", 105 | "source-map-loader": "^1.0.0", 106 | "style-loader": "^3.3.1", 107 | "ts-jest": "^28.0.2", 108 | "ts-loader": "^9.2.8", 109 | "ts-node": "^10.7.0", 110 | "typescript": "^4.6.3", 111 | "webpack": "^5.72.0", 112 | "webpack-cli": "^4.9.2", 113 | "webpack-dev-server": "^4.8.1" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /scripts/deploy-eb.sh: -------------------------------------------------------------------------------- 1 | echo "Processing deploy-eb.sh" 2 | # Set EB BUCKET as env variable 3 | EB_BUCKET=elasticbeanstalk-us-east-2-070809985305 4 | # Set the default region for aws cli 5 | aws configure set default.region us-east-2 6 | # Log in to ECR 7 | eval $(aws ecr get-login --no-include-email --region us-east-2) 8 | # Build docker image based on our production Dockerfile 9 | docker build -t accumulus-deploy . 10 | # tag the image with the Travis-CI SHA 11 | docker tag accumulus-deploy:latest 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-deploy:$TRAVIS_COMMIT 12 | # Push built image to ECS 13 | docker push 070809985305.dkr.ecr.us-east-2.amazonaws.com/accumulus-deploy:$TRAVIS_COMMIT 14 | # Use the linux sed command to replace the text '' in our Dockerrun file with the Travis-CI SHA key 15 | sed -i='' "s//$TRAVIS_COMMIT/" Dockerrun.aws.json 16 | # Zip up our codebase, along with modified Dockerrun and our .ebextensions directory 17 | zip -r accumulus-deploy.zip Dockerrun.aws.json .ebextensions 18 | # Upload zip file to s3 bucket 19 | aws s3 cp accumulus-deploy s3://$EB_BUCKET/accumulus-deploy.zip 20 | # Create a new application version with new Dockerrun 21 | aws elasticbeanstalk create-application-version --application-name accumulus-main --version-label $TRAVIS_COMMIT --source-bundle S3Bucket=$EB_BUCKET,S3Key=accumulus-deploy.zip 22 | # Update environment to use new version number 23 | aws elasticbeanstalk update-environment --environment-name accumulus-main-env --version-label $TRAVIS_COMMIT -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | # install AWS SDK 2 | pip install awscli 3 | export PATH=$PATH:$HOME/.local/bin 4 | 5 | # install necessary dependency for ecs-deploy 6 | add-apt-repository ppa:eugenesan/ppa 7 | apt-get update 8 | apt-get install jq -y 9 | 10 | # install ecs-deploy 11 | curl https://raw.githubusercontent.com/silinternational/ecs-deploy/master/ecs-deploy | \ 12 | sudo tee -a /usr/bin/ecs-deploy 13 | sudo chmod +x /usr/bin/ecs-deploy 14 | 15 | # Set the default region for aws cli 16 | aws configure set default.region us-east-2 17 | eval $(aws ecr get-login --no-include-email --region us-east-2) 18 | 19 | # build the docker image and push to an image repository 20 | docker build -t accumulus/accumulus-production . 21 | docker tag accumulus/accumulus-production:latest $IMAGE_REPO_URL:latest 22 | docker push $IMAGE_REPO_URL:latest 23 | 24 | # update an AWS ECS service with the new image 25 | ecs-deploy -c $CLUSTER_NAME -n $SERVICE_NAME -i $IMAGE_REPO_URL:latest --force-new-deployment --timeout 600 -------------------------------------------------------------------------------- /server/auth/auth.ts: -------------------------------------------------------------------------------- 1 | //placeholder for authentication 2 | export {}; 3 | -------------------------------------------------------------------------------- /server/controllers/aws/cacheController.ts: -------------------------------------------------------------------------------- 1 | import Redis from 'ioredis'; 2 | import 'dotenv/config'; 3 | import { middlewareFunction } from '../../types' 4 | 5 | // Redis instance for cache 6 | const cache = new Redis({ 7 | host: process.env.REDIS_URL, 8 | port: Number(process.env.REDIS_PORT), 9 | password: process.env.REDIS_PASSWORD, 10 | }) 11 | 12 | // redis get function to look up arn + specific query in cache 13 | export const cacheGet: middlewareFunction = async (req, res, next) => { 14 | try { 15 | // take user specific information and the endpoint they are accessing as keys for redis 16 | const { arn } = res.locals.userData; 17 | const queryString = req.originalUrl; 18 | const { region } = req.body 19 | 20 | // variable to check whether user wants to bypass retrieving from cache and get fresh data 21 | let { sync } = req.body 22 | 23 | const cachedData = await cache.get(`${arn}${queryString}${region}`) 24 | 25 | // bypass if cached data is found and sync is true 26 | if(typeof cachedData ==='string' && !sync) { 27 | // console.log('found cached data for ', req.originalUrl) 28 | res.status(200).send(JSON.parse(cachedData)) 29 | } else next(); 30 | } catch (error) { 31 | console.log(error) 32 | next(error) 33 | } 34 | } 35 | 36 | // redis set function to set arn + specific query, TTL, and the cached value 37 | export const cacheSet:middlewareFunction = async (req, res, next) => { 38 | //declare map of Time to expire ( in seconds ) 39 | const timeScale: { [key: string]: number } = { 40 | '7d': 24 * 60 * 60, 41 | '24hr': 12 * 60 * 60, 42 | '12hr': 60 * 60, 43 | '1hr': 30 * 60, 44 | } 45 | 46 | if (!req.params.period) req.params.period = '24hr' // default to 1 for 47 | console.log('setting into cache') 48 | cache.set(`${res.locals.userData.arn}${req.originalUrl}${req.body.region}`, JSON.stringify(res.locals.toBeCached), 'EX', timeScale[req.params.period]) 49 | console.log('cache is set') 50 | next() 51 | } -------------------------------------------------------------------------------- /server/controllers/aws/costController.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import formatController from './formatController'; 3 | 4 | const costController: any = {}; 5 | 6 | costController.calcCost = ( 7 | invocations: number, 8 | duration: number, 9 | memory: number, 10 | ephemeral: number, 11 | architectures: string, 12 | region: string 13 | ): number => { 14 | // x86 Price - $0.0000166667 for every GB-second - $0.20 per 1M requests 15 | // Arm Price - $0.0000133334 for every GB-second - $0.20 per 1M requests 16 | 17 | // Ephemeral Storage - $0.0000000309 for every GB-second 18 | 19 | // Gb-s = duration[ms] / 1000 * (memory allocated / 1024) [Gb] 20 | // cost = 0.0000166667 * GB-s + 0.0000000309 * GB-s + Invocations / 1000000 * 0.20 21 | 22 | let ephFactor = region === 'us-west-1' ? 0.000000037 : 0.0000000309; 23 | let reqFactor = 0.2; 24 | let memFactor = architectures === 'x86_64' ? 0.0000166667 : 0.0000133334; 25 | let GbsecsCompute = (duration / 1000) * (memory / 1024); 26 | let GbsecsStorage = (duration / 1000) * (ephemeral / 1024); 27 | let cost = 28 | memFactor * GbsecsCompute + 29 | ephFactor * GbsecsStorage + 30 | (invocations / 1000000) * reqFactor; 31 | return cost; 32 | }; 33 | 34 | costController.calcCostEachLambda = async ( 35 | 36 | req: express.Request, 37 | res: express.Response, 38 | next: express.NextFunction 39 | ) => { 40 | const { region } = req.body; 41 | // Data[i] signature 42 | // interface IData { 43 | // id: number, 44 | // xkey: string, 45 | // LoginUser: number, 46 | // InvokeLambdaFuncs: number, 47 | // ProcessPurchase: number, 48 | // RegisterUser: number, 49 | // GetMetrics: number, 50 | // helloWorldFunction: number, 51 | // AnalyzeData: number, 52 | // } 53 | 54 | // Invocation data is being mutated!!! 55 | const cost = res.locals.Invocations; 56 | cost.title = 'Lamda Costs'; 57 | 58 | const costData = res.locals.Invocations.series[0].data; 59 | const invocationData = res.locals.Invocations.series[0].data; 60 | const durationData = res.locals.Duration.series[0].data; 61 | const lambdaFuncs = res.locals.lambdaFunctions; 62 | 63 | for (let i = 0; i < costData.length; i++) { 64 | let total = 0; 65 | for (let j = 0; j < lambdaFuncs.length; j++) { 66 | const name = lambdaFuncs[j].name; 67 | // const architectures = lambdaFuncs[j].architectures[0]; 68 | const architectures = 'x86_64'; // we hard coded this because architectures was coming back as 'undefined' for us-west-1 69 | const memory = lambdaFuncs[j].memoryAllocated; 70 | const ephemeral = lambdaFuncs[j].ephemeral.Size; 71 | let calcCost = costController.calcCost( 72 | invocationData[i][name], 73 | durationData[i][name], 74 | memory, 75 | ephemeral, 76 | architectures, 77 | region 78 | ); 79 | const roundedCost = calcCost.toPrecision(4) 80 | costData[i][name] = roundedCost; 81 | total += calcCost; 82 | } 83 | 84 | costData[i].value = total.toPrecision(4); 85 | } 86 | res.locals.costData = cost; 87 | res.locals.toBeCached = res.locals.costData 88 | next(); 89 | }; 90 | 91 | export default costController; 92 | 93 | // Client Pricing API 94 | 95 | // //this is an array containing all listed lambdaFunctions 96 | // const { lambdaFunctions } = res.locals; 97 | // const { credentials } = res.locals; 98 | // const { region } = req.body; 99 | // //get :period from req params 100 | // const totalCostPeriod = req.params.period; 101 | // console.log(lambdaFunctions); 102 | // console.log('entered costController.calcCostTotalLambda'); 103 | // // ----------describe-services params---------WHAT GOES HERE?!?!?!?! 104 | // // const params = { 105 | // // FormatVersion: ‘aws_v1’, 106 | // // MaxResults: 100, 107 | // // // NextToken: ‘ALL’, 108 | // // ServiceCode: ‘AmazonEC2’ 109 | // // }; 110 | // // const costClient = new PricingClient({ 111 | // // region, 112 | // // credentials, 113 | // // }); 114 | // // const command = new DescribeServicesCommand(params); 115 | // // console.log(command); 116 | // try { 117 | // console.log('entered try block in calcCostTotalLambda'); 118 | // // const costAllFuncResult = await costClient.send(command) 119 | // //.then(data => console.log(data)); 120 | // // console.log(‘costController.calcLambdaCostAll COST ALL FUNC DATA: ’, costAllFuncResult); 121 | // const totalCost = 'placeholder'; //organize results into usable data 122 | // res.locals.totalCost = totalCost; 123 | // next(); 124 | // } catch (err) { 125 | // console.log('costController.calcLamdaCostAll failed to work...'); 126 | // return next(err); 127 | // } 128 | -------------------------------------------------------------------------------- /server/controllers/aws/credController.ts: -------------------------------------------------------------------------------- 1 | import stsClient from './stsController'; 2 | import * as types from '../../types'; 3 | import { AssumeRoleCommand } from '@aws-sdk/client-sts'; 4 | 5 | const credController: Record = {}; 6 | 7 | credController.getCreds = async (req, res, next) => { 8 | const { arn, externalId } = res.locals.userData; 9 | const roleParams = { 10 | RoleArn: arn, 11 | RoleSessionName: 'DoesThisNotMatter', 12 | ExternalId: externalId, 13 | }; 14 | 15 | try { 16 | const assumedRole = await stsClient.send(new AssumeRoleCommand(roleParams)); 17 | if (assumedRole.Credentials !== undefined) { 18 | const accessKeyId = assumedRole.Credentials.AccessKeyId; 19 | const secretAccessKey = assumedRole.Credentials.SecretAccessKey; 20 | const sessionToken = assumedRole.Credentials.SessionToken; 21 | res.locals.credentials = { accessKeyId, secretAccessKey, sessionToken }; 22 | return next(); 23 | } else { 24 | console.log( 25 | 'credController.getCreds assumedRole.Credentials is undefined' 26 | ); 27 | return next(); 28 | } 29 | } catch (err) { 30 | console.log( 31 | 'credController.getCreds ERROR: fail to send role params to stsClient' 32 | ); 33 | next(err); 34 | } 35 | }; 36 | 37 | export default credController; 38 | -------------------------------------------------------------------------------- /server/controllers/aws/formatController.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import moment from 'moment'; 3 | 4 | const formatController: any = {}; 5 | 6 | formatController.timeRangePeriod = { 7 | // dimension is seconds (i.e. 60 is 60s) 8 | minutes: 60, // 1 minute granularity 9 | hours: 60 * 60, // 1 hour granularity 10 | days: 60 * 60 * 24, // 1 day granularity --> returns 30 data points for @period='30d' 11 | }; 12 | 13 | formatController.timeRoundMultiplier = { 14 | minutes: 5, //the EndTime time stamps will be rounded to nearest 5 minutes 15 | hours: 15, //rounded to nearest 15 minutes 16 | days: 60, // rounded to nearest hour 17 | }; 18 | 19 | formatController.timeRangeMultiplier = { 20 | minutes: 60, //the EndTime time stamps will be rounded to nearest 5 minutes 21 | hours: 3600, //rounded to nearest 15 minutes 22 | days: 86400, // rounded to nearest hour 23 | }; 24 | 25 | formatController.formatQueryTimes = ( 26 | timeRangeUnits: number, 27 | timeRangeNum: string 28 | ) => { 29 | // Format start and end time for sdk query func 30 | const timeRound = 31 | formatController.timeRoundMultiplier[timeRangeUnits.toString()]; 32 | const EndTime = 33 | Math.round(new Date().getTime() / 1000 / 60 / +timeRound) * 60 * +timeRound; //current time in Unix TimeStamp 34 | const StartTime = 35 | EndTime - 36 | +timeRangeNum * 37 | +formatController.timeRangeMultiplier[timeRangeUnits.toString()]; 38 | const period = formatController.timeRangePeriod[timeRangeUnits.toString()]; 39 | 40 | return [StartTime, EndTime, period]; 41 | }; 42 | 43 | formatController.periodDefinitions = { 44 | '1hr': [60, 'minutes'], 45 | '12hr': [12, 'hours'], 46 | '24hr': [24, 'hours'], 47 | '7d': [7, 'days'], 48 | '14d': [14, 'days'], 49 | '30d': [30, 'days'], 50 | '3mo': [90, 'days'], 51 | '6mo': [180, 'days'], 52 | '1yr': [30, 'days'], 53 | }; 54 | 55 | formatController.formatXAxisLabel = (timeStamp: Date, graphUnits: string) => { 56 | if (graphUnits === 'days') { 57 | return moment(timeStamp).format('MMM DD'); 58 | } else if (graphUnits === 'hours') { 59 | return moment(timeStamp).format('HH:00'); 60 | } else if (graphUnits === 'minutes') { 61 | return moment(timeStamp).format('HH:mm'); 62 | } 63 | return; 64 | }; 65 | 66 | formatController.aggregateFuncByPeriodConversion = ( 67 | period: string, 68 | curDate: Date, 69 | endDate: Date 70 | ) => { 71 | interface IPeriodConvert { 72 | '30min'?: number[]; 73 | '1hr'?: number[]; 74 | '24hr'?: number[]; 75 | '7d'?: number[]; 76 | '14d'?: number[]; 77 | '30d'?: number[]; 78 | '3mo'?: number[]; 79 | '6mo'?: number[]; 80 | '1yr'?: number[]; 81 | } 82 | const periodConvert: IPeriodConvert = { 83 | '30d': [ 84 | curDate.getDate(), 85 | endDate.getDate(), 86 | curDate.getMonth(), 87 | endDate.getMonth(), 88 | ], 89 | }; 90 | 91 | return periodConvert[period as '30d']; 92 | }; 93 | 94 | formatController.formatCWLambdaMetricAll = ( 95 | timeRangeNum: number, 96 | timeRangeUnits: string, 97 | metricName: string, 98 | metricStat: string 99 | ) => { 100 | // Formatting & defining the start-end times that the AWS-query pulls metric data from 101 | const [StartTime, EndTime, period] = formatController.formatQueryTimes( 102 | timeRangeUnits, 103 | timeRangeNum 104 | ); 105 | 106 | // Format Query 107 | return { 108 | StartTime: new Date(StartTime * 1000), 109 | EndTime: new Date(EndTime * 1000), 110 | LabelOptions: { 111 | Timezone: '-0400', 112 | }, 113 | MetricDataQueries: [ 114 | { 115 | Id: `m${metricName}_AllLambdaFunc`, 116 | Label: `Lambda ${metricName} All Functions`, 117 | ReturnData: false, 118 | MetricStat: { 119 | Metric: { 120 | Namespace: 'AWS/Lambda', 121 | MetricName: `${metricName}`, 122 | }, 123 | Period: period, 124 | Stat: metricStat, 125 | }, 126 | }, 127 | { 128 | Id: `m${metricName}`, 129 | Label: `Lambda ${metricName} All Functions`, 130 | Expression: `FILL(METRICS(), 0)`, 131 | }, 132 | ], 133 | }; 134 | }; 135 | 136 | formatController.formatCWLambdaMetricByFunc = ( 137 | timeRangeNum: number, 138 | timeRangeUnits: string, 139 | metricName: string, 140 | metricStat: string, 141 | funcNames: [] 142 | ) => { 143 | const [StartTime, EndTime, period] = formatController.formatQueryTimes( 144 | timeRangeUnits, 145 | timeRangeNum 146 | ); 147 | 148 | // Init the baseline object params 149 | const lambdaMetricQueryParamsBase = { 150 | StartTime: new Date(StartTime * 1000), 151 | EndTime: new Date(EndTime * 1000), 152 | LabelOptions: { 153 | Timezone: '-0400', 154 | }, 155 | // MetricDataQueries: [], 156 | }; 157 | 158 | const metricDataQueryByFunc: object[] = []; 159 | 160 | // Iterate over lambda funcs and format 161 | funcNames.forEach((func, index) => { 162 | const metricDataQuery = { 163 | Id: `m${index}`, 164 | // Label: `Lambda ${metricName} ${func}`, 165 | Label: `${func}`, 166 | Function: func, 167 | ReturnData: false, 168 | MetricStat: { 169 | Metric: { 170 | Namespace: 'AWS/Lambda', 171 | MetricName: `${metricName}`, 172 | Dimensions: [ 173 | { 174 | Name: 'FunctionName', 175 | Value: `${func}`, 176 | }, 177 | ], 178 | }, 179 | Period: period, 180 | Stat: metricStat, 181 | }, 182 | }; 183 | const queryWithMissingFilled = { 184 | Id: `m${index}fill`, 185 | Label: func, 186 | Expression: `FILL(m${index}, 0)`, 187 | }; 188 | metricDataQueryByFunc.push(queryWithMissingFilled); 189 | metricDataQueryByFunc.push(metricDataQuery); 190 | }); 191 | const metricParamsByFunc = { 192 | ...lambdaMetricQueryParamsBase, 193 | MetricDataQueries: metricDataQueryByFunc, 194 | }; 195 | 196 | return metricParamsByFunc; 197 | }; 198 | 199 | formatController.monthConversion = { 200 | 0: 'January', 201 | 1: 'February', 202 | 2: 'March', 203 | 3: 'April', 204 | 4: 'May', 205 | 5: 'June', 206 | 6: 'July', 207 | 7: 'August', 208 | 8: 'September', 209 | 9: 'October', 210 | 10: 'November', 211 | 11: 'December', 212 | }; 213 | 214 | formatController.logPeriodConversion = { 215 | '30min': new Date(new Date().setMinutes(new Date().getMinutes() - 30)).valueOf(), 216 | '1hr': new Date(new Date().setMinutes(new Date().getMinutes() - 60)).valueOf(), 217 | '12hr': new Date(new Date().setMinutes(new Date().getMinutes() - 720)).valueOf(), 218 | '24hr': new Date(new Date().setDate(new Date().getDate() - 1)).valueOf(), 219 | '7d': new Date(new Date().setDate(new Date().getDate() - 7)).valueOf(), 220 | '14d': new Date(new Date().setDate(new Date().getDate() - 14)).valueOf(), 221 | '30d': new Date(new Date().setDate(new Date().getDate() - 30)).valueOf(), 222 | }; 223 | 224 | export default formatController; 225 | -------------------------------------------------------------------------------- /server/controllers/aws/lambdaController.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import express from 'express'; 3 | import lambda from '@aws-sdk/client-lambda'; 4 | import * as types from '../../types'; 5 | 6 | const lambdaController: any = {}; 7 | // const lambdaController: Record = {}; 8 | 9 | interface LambdaFunctionResponse { 10 | name: string; 11 | description: string; 12 | architectures: string[]; 13 | size: number; 14 | memoryAllocated: number; 15 | ephemeral: object; 16 | timeout: number; 17 | lastModified: string; 18 | } 19 | 20 | lambdaController.getFunctions = ( 21 | req: express.Request, 22 | res: express.Response, 23 | next: express.NextFunction 24 | ) => { 25 | const { credentials } = res.locals; 26 | const { region } = req.body; 27 | const client = new lambda.LambdaClient({ 28 | region, 29 | credentials, 30 | }); 31 | const iam = { FunctionVersion: 'ALL' }; 32 | 33 | // Send request to get all lambda funcs in account for region 34 | try { 35 | const listOfLambdaFuncs = client 36 | .send(new lambda.ListFunctionsCommand(iam)) 37 | .then((data) => { 38 | if (typeof data.Functions === 'object') { 39 | const filteredFunctions = data.Functions.filter((el) => el.FunctionName !== 'InvokeLambdaFuncs') 40 | res.locals.funcNames = filteredFunctions.map((el) => el.FunctionName); 41 | res.locals.toBeCached = res.locals.funcNames; 42 | let funcData = []; 43 | for (let i = 0; i < filteredFunctions.length; i++) { 44 | const formattedResponse: LambdaFunctionResponse = { 45 | name: filteredFunctions[i].FunctionName!, 46 | description: filteredFunctions[i].Description!, 47 | architectures: filteredFunctions[i].Architectures!, 48 | size: filteredFunctions[i].CodeSize!, // (in bytes) 49 | memoryAllocated: filteredFunctions[i].MemorySize!, // (in MB) 50 | ephemeral: filteredFunctions[i].EphemeralStorage!, // (in MB) 51 | timeout: filteredFunctions[i].Timeout!, 52 | lastModified: filteredFunctions[i].LastModified!, 53 | }; 54 | funcData.push(formattedResponse); 55 | } 56 | res.locals.lambdaFunctions = funcData; 57 | } 58 | return next(); 59 | }); 60 | } catch (err) { 61 | return next(err); 62 | } 63 | }; 64 | 65 | lambdaController.metrics = { 66 | invocations: 'Invocations', 67 | duration: 'Duration', 68 | errors: 'Errors', 69 | throttles: 'Throttles', 70 | concurrency: 'Concurrency', 71 | }; 72 | 73 | export default lambdaController; 74 | -------------------------------------------------------------------------------- /server/controllers/aws/stepFuncs/formatCW.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | const timeRangePeriod: { [key: string]: string | number } = { 4 | minutes: 60, //60 seconds 5 | hours: 300, //300 secs 6 | days: 3600, // 1 hour 7 | }; 8 | 9 | const timeRoundMultiplier: { [key: string]: string | number } = { 10 | minutes: 5, //the EndTime time stamps will be rounded to nearest 5 minutes 11 | hours: 15, //rounded to nearest 15 minutes 12 | days: 60, // rounded to nearest hour 13 | }; 14 | 15 | const timeRangeMultiplier: { [key: string]: string | number } = { 16 | minutes: 60, //the EndTime time stamps will be rounded to nearest 5 minutes 17 | hours: 3600, //rounded to nearest 15 minutes 18 | days: 86400, // rounded to nearest hour 19 | }; 20 | 21 | const formatCW: any = {}; 22 | 23 | formatCW.formatCWStateMetricByFunc = ( 24 | timeRangeNum: number, 25 | timeRangeUnits: string, 26 | metricName: string, 27 | metricStat: string, 28 | stateArn: string 29 | ) => { 30 | const timeRound = timeRoundMultiplier[timeRangeUnits]; 31 | //define the End and Start times in UNIX time Stamp format for getMetricsData method 32 | //Rounded off to nearest timeRoundMultiplier 33 | const endTime = 34 | Math.round(new Date().getTime() / 1000 / 60 / +timeRound) * 60 * +timeRound; //current time in Unix TimeStamp 35 | const startTime = 36 | endTime - timeRangeNum * +timeRangeMultiplier[timeRangeUnits]; 37 | 38 | const period = timeRangePeriod[timeRangeUnits]; 39 | 40 | // Init the baseline object params 41 | const lambdaMetricQueryParamsBase = { 42 | StartTime: new Date(startTime * 1000), 43 | EndTime: new Date(endTime * 1000), 44 | LabelOptions: { 45 | Timezone: '-0400', 46 | }, 47 | // MetricDataQueries: [], 48 | }; 49 | 50 | const metricDataQueryByFunc: object[] = []; 51 | // Iterate over lambda funcs and format 52 | // stateArn.forEach((func, index) => { 53 | let metricDataQuery = { 54 | // Id: `m${index}`, 55 | Id: `m1`, 56 | Label: `Lambda ${metricName} ${stateArn}`, 57 | MetricStat: { 58 | Metric: { 59 | Namespace: 'AWS/State', 60 | MetricName: `${metricName}`, 61 | Dimensions: [ 62 | { 63 | Name: 'StateMachineArn', 64 | Value: `${stateArn}`, 65 | }, 66 | ], 67 | }, 68 | Period: period, 69 | Stat: metricStat, 70 | }, 71 | }; 72 | metricDataQueryByFunc.push(metricDataQuery); 73 | // }); 74 | 75 | const metricParamsByFunc = { 76 | ...lambdaMetricQueryParamsBase, 77 | MetricDataQueries: metricDataQueryByFunc, 78 | }; 79 | 80 | return metricParamsByFunc; 81 | }; 82 | 83 | export default formatCW; 84 | -------------------------------------------------------------------------------- /server/controllers/aws/stepFuncs/stepController.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import express from 'express'; 3 | import formatCW from './formatCW'; 4 | import { 5 | CloudWatchClient, 6 | GetMetricDataCommand, 7 | } from '@aws-sdk/client-cloudwatch'; 8 | 9 | const stepController: any = {}; 10 | 11 | stepController.getStateMetricByFunc = async ( 12 | req: express.Request, 13 | res: express.Response, 14 | next: express.NextFunction 15 | ) => { 16 | const { credentials } = res.locals; 17 | const { region, stateArn } = req.body; 18 | const graphMetricName = req.params.metric; 19 | const graphMetricStat = req.params.stat; 20 | const periodSelection = req.params.period; 21 | let graphPeriod; 22 | let graphUnits; 23 | 24 | if (periodSelection === '30min') { 25 | [graphPeriod, graphUnits] = [30, 'minutes']; 26 | } else if (periodSelection === '1hr') { 27 | [graphPeriod, graphUnits] = [60, 'minutes']; 28 | } else if (periodSelection === '24hr') { 29 | [graphPeriod, graphUnits] = [24, 'hours']; 30 | } else if (periodSelection === '7d') { 31 | [graphPeriod, graphUnits] = [7, 'days']; 32 | } else if (periodSelection === '14d') { 33 | [graphPeriod, graphUnits] = [14, 'days']; 34 | } else if (periodSelection === '30d') { 35 | [graphPeriod, graphUnits] = [30, 'days']; 36 | } else if (periodSelection === '3mon') { 37 | [graphPeriod, graphUnits] = [90, 'days']; 38 | } else if (periodSelection === '6mon') { 39 | [graphPeriod, graphUnits] = [180, 'days']; 40 | } else if (periodSelection === '1y') { 41 | [graphPeriod, graphUnits] = [30, 'days']; 42 | } 43 | 44 | const cwClient = new CloudWatchClient({ 45 | region, 46 | credentials, 47 | }); 48 | 49 | const metricAllFuncInputParams = formatCW.formatCWStateMetricByFunc( 50 | graphPeriod, 51 | graphUnits, 52 | graphMetricName, 53 | graphMetricStat, 54 | stateArn 55 | ); 56 | 57 | // Send API request 58 | try { 59 | const query = new GetMetricDataCommand(metricAllFuncInputParams); 60 | const metricAllFuncResult = await cwClient.send(query); 61 | console.log( 62 | 'cwController.getStateMetricByFunc METRIC ALL FUNC DATA: ', 63 | metricAllFuncResult 64 | ); 65 | 66 | console.log('metricAllFuncInputParams: ', metricAllFuncInputParams) 67 | console.log('metricAllFuncResults: ', metricAllFuncResult) 68 | let metricAllFuncData = 69 | metricAllFuncResult.MetricDataResults![0].Timestamps!.map( 70 | (timeStamp, index) => { 71 | return { 72 | x: timeStamp, 73 | y: metricAllFuncResult.MetricDataResults![0].Values![index], 74 | }; 75 | } 76 | ); 77 | 78 | const metricMaxValue = Math.max( 79 | ...metricAllFuncResult.MetricDataResults![0].Values!, 80 | 0 81 | ); 82 | 83 | const metricAllFuncOutput = { 84 | title: metricAllFuncResult.MetricDataResults![0].Label, 85 | options: { 86 | startTime: metricAllFuncInputParams.StartTime, 87 | endTime: metricAllFuncInputParams.EndTime, 88 | graphPeriod, 89 | graphUnits, 90 | metricMaxValue, 91 | }, 92 | }; 93 | 94 | res.locals.lambdaMetricsAllFuncs = metricAllFuncOutput; 95 | console.log( 96 | 'cwController.getStateMetricByFunc FORMATTED METRIC ALL FUNC DATA', 97 | metricAllFuncOutput 98 | ); 99 | 100 | next(); 101 | } catch (err) { 102 | console.log( 103 | 'cwController.getStateMetricByFunc failed to GetMetricDataCommand' 104 | ); 105 | return next(err); 106 | } 107 | }; 108 | 109 | export default stepController; 110 | -------------------------------------------------------------------------------- /server/controllers/aws/stsController.ts: -------------------------------------------------------------------------------- 1 | import { STSClient } from '@aws-sdk/client-sts'; 2 | import utilController from './utilController'; 3 | 4 | let account = utilController.getAwsCreds; 5 | let { region, credentials } = account; 6 | 7 | const stsClient = new STSClient({ 8 | region: region, 9 | credentials: credentials, 10 | }); 11 | 12 | export default stsClient; 13 | -------------------------------------------------------------------------------- /server/controllers/aws/utilController.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | const utilController: any = {}; 4 | 5 | utilController.getAwsCreds = () => { 6 | const account = { 7 | region: process.env.AWS_REGION, 8 | credentials: { 9 | accessKeyId: process.env.AWS_ACCESS_KEY, 10 | secretAccessKey: process.env.AWS_SECRET_KEY, 11 | } 12 | } 13 | return account; 14 | } 15 | 16 | export default utilController; -------------------------------------------------------------------------------- /server/controllers/cookieController.ts: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | 3 | const cookieController: Record = {}; 4 | 5 | cookieController.setCookieCredentials = (req, res, next) => { 6 | res.cookie('name', res.locals.confirmation.name, { maxAge: 900000 }); 7 | res.cookie('arn', res.locals.confirmation.arn, { maxAge: 900000 }); 8 | res.cookie('externalId', res.locals.confirmation.externalId, { 9 | maxAge: 900000, 10 | }); 11 | res.cookie('region', res.locals.confirmation.region, { maxAge: 900000 }); 12 | return next(); 13 | }; 14 | 15 | cookieController.getCookieCredentials = (req, res, next) => { 16 | const { name, arn, externalId, region } = req.cookies; 17 | res.locals.userData = { 18 | name, 19 | arn, 20 | externalId, 21 | region, 22 | }; 23 | return next(); 24 | }; 25 | 26 | cookieController.resetCookieCredentials = (req, res, next) => { 27 | const { name, arn, externalId, region } = req.cookies; 28 | res.clearCookie('name'); 29 | res.clearCookie('arn'); 30 | res.clearCookie('externalId'); 31 | res.clearCookie('region'); 32 | return next(); 33 | }; 34 | 35 | export default cookieController; 36 | -------------------------------------------------------------------------------- /server/controllers/userController.ts: -------------------------------------------------------------------------------- 1 | import User from '../database/db.js'; 2 | import * as types from '../types'; 3 | 4 | const userController: Record = {}; 5 | 6 | // Create a new user in database 7 | userController.createUser = async (req, res, next) => { 8 | try { 9 | const { name, email, password, arn, externalId, region } = req.body; 10 | // DB create query 11 | const newUser = await User.create({ 12 | name, 13 | email, 14 | password, 15 | arn, 16 | externalId, 17 | region, 18 | }); 19 | console.log('userController.createUser user created yay'); 20 | res.locals.confirmation = { 21 | userCreated: true, 22 | name: newUser.name, 23 | arn: newUser.arn, 24 | externalId: newUser.externalId, 25 | region: newUser.region, 26 | }; 27 | return next(); 28 | } catch (err) { 29 | // Entry field missing or Non-unique email 30 | console.log( 31 | 'userController.createUser ERROR: sign up failed, missing field or non-unique email' 32 | ); 33 | return next(err); 34 | } 35 | }; 36 | 37 | // Authenticate user from database 38 | userController.verifyUser = async (req, res, next) => { 39 | try { 40 | const { email, password } = req.body; 41 | // DB read query 42 | const user = await User.findOne({ email }); 43 | // Verify password 44 | //is there something wrong with this logic, or is the front end handling async incorrectly? 45 | if (user !== null) { 46 | if (await user.validatePassword(password)) { 47 | // Correct password 48 | res.locals.confirmation = { 49 | success: true, 50 | name: user.name, 51 | arn: user.arn, 52 | externalId: user.externalId, 53 | region: user.region, 54 | }; 55 | } else { 56 | // Incorrect password 57 | console.log('userController.verifyUser ERROR: wrong password'); 58 | res.locals.confirmation = { 59 | success: false, 60 | }; 61 | } 62 | return next(); 63 | } else { 64 | console.log('userController.verifyUser ERROR: email is not registered'); 65 | res.locals.confirmation = { 66 | success: false, 67 | }; 68 | return next(); 69 | } 70 | } catch (err) { 71 | // Email not found 72 | console.log('userController.verifyUser ERROR: email is not registered'); 73 | return next({ err }); 74 | } 75 | }; 76 | 77 | // userController.printTwo = (req, res, next) => { 78 | // return 'two'; 79 | // } 80 | 81 | export default userController; 82 | 83 | export const printTwo: Function = () => { 84 | return 'two'; 85 | } 86 | -------------------------------------------------------------------------------- /server/data/testData.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/server/data/testData.json -------------------------------------------------------------------------------- /server/database/db.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import bcrypt from 'bcryptjs'; 3 | const { Schema } = mongoose; 4 | 5 | // Interface for user 6 | interface IUser { 7 | email: string; 8 | password: string; 9 | name: string; 10 | arn: string; 11 | externalId: string; 12 | region: string; 13 | validatePassword: (passwordTry: string) => Promise; 14 | } 15 | 16 | // Schema for mongodb 17 | 18 | const userSchema = new Schema({ 19 | email: { type: String, required: true, unique: true }, 20 | password: { type: String, required: true }, 21 | name: { type: String, required: true }, 22 | arn: { type: String, required: true }, 23 | externalId: { type: String, required: true }, 24 | region: { type: String, required: true }, 25 | }); 26 | 27 | userSchema.methods.validatePassword = async function (passwordTry: string) { 28 | return bcrypt.compare(passwordTry, this.password); 29 | }; 30 | 31 | const SALT_WORK_FACTOR: number = 10; 32 | const salt = bcrypt.genSaltSync(SALT_WORK_FACTOR); 33 | 34 | userSchema.pre('save', function (next) { 35 | const user = this; 36 | bcrypt.hash(user.password, 10, function (err, hash) { 37 | if (err) return next(err); 38 | user.password = hash; 39 | next(); 40 | }); 41 | }); 42 | 43 | const User = mongoose.model('User', userSchema); 44 | 45 | export default User; 46 | -------------------------------------------------------------------------------- /server/routers/awsRouter.ts: -------------------------------------------------------------------------------- 1 | import express, { Response, Request, NextFunction } from 'express'; 2 | import lambdaController from '../controllers/aws/lambdaController'; 3 | import credController from '../controllers/aws/credController'; 4 | import cwController from '../controllers/aws/cloudwatchController'; 5 | import costController from '../controllers/aws/costController'; 6 | import logController from '../controllers/aws/logController'; 7 | import stepController from '../controllers/aws/stepFuncs/stepController'; 8 | import cookieController from '../controllers/cookieController'; 9 | import analysisController from '../controllers/aws/analysisController'; 10 | import * as cacheController from '../controllers/aws/cacheController'; 11 | // import * as types from '../types'; 12 | 13 | const router = express.Router(); 14 | 15 | /* Lambda Function Names and Config Settings */ 16 | router.get( 17 | '/', 18 | (req: express.Request, res: express.Response) => { 19 | console.log('SHOULD SHOW COOKIES HERE:') 20 | res.status(200).json({}) 21 | } 22 | ); 23 | 24 | router.post( 25 | '/lambdaNames', 26 | cookieController.getCookieCredentials, 27 | cacheController.cacheGet, 28 | credController.getCreds, // credentials go into res.locals.credentials 29 | lambdaController.getFunctions, // function details go into res.locals.lambdaFunctions 30 | cacheController.cacheSet, 31 | (req: express.Request, res: express.Response) => { 32 | // console.log('SHOULD SHOW COOKIES HERE:', req.cookies) 33 | res.status(200).json(res.locals.funcNames); 34 | } 35 | ); 36 | 37 | /* Cloudwatch Metrics */ 38 | router.post( 39 | '/metricsTotalFuncs/:metric/:period/:stat', 40 | cookieController.getCookieCredentials, 41 | cacheController.cacheGet, 42 | credController.getCreds, 43 | cwController.getMetricsTotalLambda, 44 | cacheController.cacheSet, 45 | (req: Request, res: Response) => { 46 | res.status(200).json(res.locals.data); 47 | } 48 | ); 49 | 50 | router.post( 51 | '/metricsEachFunc/:metric/:period/:stat', 52 | cookieController.getCookieCredentials, 53 | cacheController.cacheGet, 54 | credController.getCreds, 55 | lambdaController.getFunctions, 56 | cwController.getMetricsEachLambda, 57 | cacheController.cacheSet, 58 | (req: Request, res: Response) => { 59 | res.status(200).json(res.locals[req.params.metric]); 60 | } 61 | ); 62 | 63 | router.post( 64 | '/rankFuncsByMetric/:metric/:period/:stat', 65 | cookieController.getCookieCredentials, 66 | cacheController.cacheGet, 67 | credController.getCreds, 68 | lambdaController.getFunctions, 69 | cwController.rankFuncsByMetric, 70 | cacheController.cacheSet, 71 | (req: Request, res: Response) => { 72 | res.status(200).json(res.locals.functionRankings); 73 | } 74 | ); 75 | 76 | /* Lambda Costs */ 77 | router.post( 78 | '/costEachFunction/:period', 79 | cookieController.getCookieCredentials, // user data goes into res.locals.userData 80 | cacheController.cacheGet, 81 | credController.getCreds, 82 | lambdaController.getFunctions, // res.locals.funcNames & res.locals.lambdaFunctions 83 | (req: Request, res: Response, next: NextFunction) => { 84 | req.params.metric = 'Invocations'; 85 | req.params.stat = 'Sum'; 86 | next(); 87 | }, 88 | cwController.getMetricsEachLambda, 89 | (req: Request, res: Response, next: NextFunction) => { 90 | req.params.metric = 'Duration'; 91 | req.params.stat = 'Sum'; 92 | next(); 93 | }, 94 | cwController.getMetricsEachLambda, 95 | costController.calcCostEachLambda, 96 | cacheController.cacheSet, 97 | (req: Request, res: Response) => { 98 | res.status(200).json(res.locals.costData); 99 | } 100 | ); 101 | 102 | /* All logs */ 103 | /* For Debugging One Function */ 104 | // router.post( 105 | // '/lambdaLogs/:function/:period', 106 | // cookieController.getCookieCredentials, 107 | // cacheController.cacheGet, 108 | // credController.getCreds, 109 | // logController.getLambdaLogsByFunc, 110 | // analysisController.calcMetrics 111 | // cacheController.cacheSet, 112 | // (req: Request, res: Response) => { 113 | // res.status(200).json(res.locals.data); 114 | // } 115 | // ); 116 | 117 | router.post( 118 | '/lambdaErrorLogsEachFunc/:period', 119 | cookieController.getCookieCredentials, 120 | cacheController.cacheGet, 121 | credController.getCreds, 122 | lambdaController.getFunctions, 123 | logController.getLambdaErrorsEachFunc, 124 | cacheController.cacheSet, 125 | (req: Request, res: Response) => { 126 | res.status(200).json(res.locals.logs); 127 | } 128 | ); 129 | 130 | // /* Log Metrics for debugging */ 131 | // router.post( 132 | // '/lambdaLogMetricsByFunc/:function/:period', 133 | // cookieController.getCookieCredentials, 134 | // cacheController.cacheGet, 135 | // credController.getCreds, 136 | // logController.getLambdaLogsByFunc, 137 | // analysisController.calcMetrics, 138 | // cacheController.cacheSet, 139 | // (req: Request, res: Response) => { 140 | // res.status(200).json(res.locals.data); 141 | // } 142 | // ); 143 | 144 | router.post( 145 | '/lambdaLogMetricsEachFunc/:period', 146 | cookieController.getCookieCredentials, 147 | cacheController.cacheGet, 148 | credController.getCreds, 149 | logController.getLambdaLogsByFunc, 150 | analysisController.calcMetrics, 151 | cacheController.cacheSet, 152 | (req: Request, res: Response) => { 153 | res.status(200).json(res.locals.data); 154 | } 155 | ); 156 | 157 | router.post( 158 | '/lambdaLogMetricsTotalFunc/:function/:period', 159 | cookieController.getCookieCredentials, 160 | cacheController.cacheGet, 161 | credController.getCreds, 162 | logController.getLambdaLogsByFunc, 163 | analysisController.calcMetrics, 164 | cacheController.cacheSet, 165 | (req: Request, res: Response) => { 166 | res.status(200).json(res.locals.data); 167 | } 168 | ); 169 | 170 | /* Log Memory Usage */ 171 | router.post( 172 | '/memoryUsageEachLambda/:period', 173 | cookieController.getCookieCredentials, 174 | cacheController.cacheGet, 175 | credController.getCreds, 176 | lambdaController.getFunctions, 177 | logController.getLambdaUsageEachFunc, 178 | analysisController.calcMemoryUsage, 179 | cacheController.cacheSet, 180 | (req: Request, res: Response) => { 181 | res.status(200).json(res.locals.data); 182 | } 183 | ); 184 | 185 | /* Takes a long time to fetch on first try! */ 186 | // router.post( 187 | // '/memoryUsageTotalLambda/:period', 188 | // cookieController.getCookieCredentials, 189 | // cacheController.cacheGet, 190 | // credController.getCreds, 191 | // lambdaController.getFunctions, 192 | // logController.getLambdaUsageEachFunc, 193 | // analysisController.calcMeanMemoryUsageTotal, 194 | // cacheController.cacheSet, 195 | // (req: Request, res: Response) => { 196 | // res.status(200).json(res.locals.data); 197 | // } 198 | // ); 199 | 200 | router.post( 201 | '/memoryUsageDiff/:period', 202 | cookieController.getCookieCredentials, 203 | cacheController.cacheGet, 204 | credController.getCreds, 205 | lambdaController.getFunctions, 206 | logController.getLambdaUsageEachFunc, 207 | analysisController.calcLambdaMemoryDiff, 208 | cacheController.cacheSet, 209 | (req: Request, res: Response) => { 210 | res.status(200).json(res.locals.data); 211 | } 212 | ); 213 | 214 | /* Step Function Metrics */ 215 | router.post( 216 | '/stateMetricsByFunc/:metric/:period/:stat', 217 | cookieController.getCookieCredentials, 218 | credController.getCreds, 219 | // cacheController.cacheGet, 220 | stepController.getStateMetricByFunc, 221 | // cacheController.cacheSet, 222 | (req: Request, res: Response) => { 223 | res.status(200).json(res.locals.lambdaMetricsAllFuncs); 224 | } 225 | ); 226 | 227 | export default router; 228 | -------------------------------------------------------------------------------- /server/routers/userRouter.ts: -------------------------------------------------------------------------------- 1 | import express, { Response, Request, NextFunction } from 'express'; 2 | const router = express.Router(); 3 | import userController from '../controllers/userController'; 4 | import cookieController from '../controllers/cookieController'; 5 | 6 | 7 | 8 | // Sign up requests 9 | router.post( 10 | '/signup', 11 | userController.createUser, 12 | cookieController.setCookieCredentials, 13 | (req: Request, res: Response): void => { 14 | res.status(200).json(res.locals.confirmation); 15 | } 16 | ); 17 | 18 | // Login requests 19 | router.post( 20 | '/login', 21 | userController.verifyUser, 22 | cookieController.setCookieCredentials, 23 | (req: Request, res: Response) => { 24 | res.status(200).json(res.locals.confirmation); 25 | } 26 | ); 27 | 28 | // Sign Out requests 29 | router.post( 30 | '/signout', 31 | // userController.verifyUser, 32 | cookieController.resetCookieCredentials, 33 | (req: Request, res: Response) => { 34 | res.status(200).json(res.locals.confirmation); 35 | } 36 | ); 37 | 38 | router.get( 39 | '/checkCoookies', 40 | cookieController.getCookieCredentials, 41 | (req: Request, res: Response): void => { 42 | res.status(200).json(res.locals.userData); 43 | } 44 | ); 45 | 46 | 47 | 48 | export default router; 49 | -------------------------------------------------------------------------------- /server/server.ts: -------------------------------------------------------------------------------- 1 | // Import boilerplate 2 | import express, { Express, Response, Request, NextFunction } from 'express'; 3 | import mongoose from 'mongoose'; 4 | import path from 'path'; 5 | import 'dotenv/config'; 6 | import cors from 'cors'; 7 | import { fileURLToPath } from 'url'; 8 | import cookieParser from 'cookie-parser'; 9 | 10 | // Router imports 11 | import userRouter from './routers/userRouter.js'; 12 | import awsRouter from './routers/awsRouter.js'; 13 | 14 | // Declare Express server and port constant 15 | const app: Express = express(); 16 | const PORT: number = 3000; 17 | 18 | // __dirname Boiler plate 19 | const __filename = fileURLToPath(import.meta.url); 20 | const __dirname = path.dirname(__filename); 21 | 22 | // Connect to MongoDB for authentication 23 | mongoose.connect(`${process.env.MONGO_URI!}`); 24 | 25 | // Parse request bodies 26 | app.use(express.json()); 27 | 28 | const corsOptions = { 29 | origin: 'http://0.0.0.0:8080', 30 | credentials: true, 31 | }; 32 | app.use(cors(corsOptions)); 33 | 34 | app.use(cookieParser()); 35 | 36 | // Serve all static files in dist directory 37 | app.use(express.static(path.join(__dirname, '../'))); 38 | 39 | // Router endpoints 40 | app.use('/api/user', userRouter); 41 | app.use('/api/aws', awsRouter); 42 | 43 | // Catch-all route handler for Express requests to an unknown route 44 | app.use((req: Request, res: Response): void => { 45 | res.status(404).send('Unknown path'); 46 | }); 47 | 48 | // Error object handler 49 | app.use((err: object, req: Request, res: Response, next: NextFunction) => { 50 | const defaultErr = { 51 | log: 'Express error handler caught unknown middleware error', 52 | status: 500, 53 | message: { err: 'An error occurred' }, 54 | }; 55 | const errorObj = { ...defaultErr, ...err }; 56 | console.log('errorObj', errorObj); 57 | return res.status(errorObj.status).json(errorObj.message); 58 | }); 59 | 60 | // Spin up server 61 | app.listen(PORT, (): void => { 62 | console.log(`Listening on ${PORT}`); 63 | }); 64 | 65 | export default app; 66 | -------------------------------------------------------------------------------- /server/types.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | 3 | // Types for Express 4 | export type middlewareFunction = ( 5 | req: express.Request, 6 | res: express.Response, 7 | next: express.NextFunction 8 | ) => void; 9 | 10 | //Type for Incoming Data 11 | export interface MemUsed{ 12 | [key:string]: string | number, 13 | }; 14 | 15 | // Types for Lambda API Response 16 | // export type lambdaFunctionResponse = ( 17 | 18 | // ) => -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import GlobalStyle from './globals'; 3 | import { HashRouter, Route, Switch } from 'react-router-dom'; 4 | import Splash from './pages/Splash'; 5 | import Login from './pages/Login'; 6 | import Dashboard from './pages/Dashboard'; 7 | import Functions from './pages/Functions'; 8 | import Memory from './pages/Memory'; 9 | import { MainGrid, Nav, Pages } from './styles'; 10 | import Navbar from './components/Navbar'; 11 | import * as fetchHelper from './fetchHelper'; 12 | import { UserProvider } from '../context/userContext'; 13 | 14 | const App = () => { 15 | const [start, setStart] = useState(false); 16 | const [userRegion, setUserRegion] = useState('us-east-1'); 17 | const [funcNames, setFuncNames] = useState([]); 18 | 19 | // --------- ALL FUNCS HOOKS 20 | // Dashboard 21 | const [totalInvocations, setTotalInvocations] = useState([]); 22 | const [totalErrors, setTotalErrors] = useState([]); 23 | const [totalCost, setTotalCost] = useState([]); 24 | 25 | // --------- BY FUNC HOOKS 26 | // Dashboard 27 | const [slowestFuncs, setSlowestFuncs] = useState([]); 28 | const [mostErroredFuncs, setMostErroredFuncs] = useState([]); 29 | const [errorMsgs, setErrorMsgs] = useState([]); 30 | // Allocation 31 | const [memUsedVsAllo, setMemUsedVsAllo] = useState([]); 32 | // Functions 33 | const [invocations, setInvocations] = useState([]); 34 | const [duration, setDuration] = useState([]); 35 | const [errors, setErrors] = useState([]); 36 | 37 | const [cost, setCost] = useState([]); 38 | const [throttles, setThrottles] = useState([]); 39 | 40 | //state to manage time metric when fetching data 41 | const [timePeriod, setTimePeriod] = useState('7d'); 42 | 43 | //state to manage resync of data -------REMEMBER TO PASS THE SYNCDATA DOWN -------------- 44 | const [syncData, setSyncData] = useState(false); 45 | 46 | const [currentView, setCurrentView] = useState('splash'); 47 | 48 | // Fires when sync button is pressed 49 | useEffect(() => { 50 | if (syncData) { 51 | // console.log('running fetch Metric ALL Functions'); 52 | fetchHelper.fetchMetricAllFunctions( 53 | setFuncNames, 54 | setTotalInvocations, 55 | setTotalErrors, 56 | setTotalCost, 57 | setSlowestFuncs, 58 | setErrorMsgs, 59 | setMostErroredFuncs, 60 | setMemUsedVsAllo, 61 | timePeriod, 62 | userRegion, 63 | syncData 64 | ); 65 | // console.log('running fetch Metric BY Functions'); 66 | fetchHelper.fetchMetricEachFunctions( 67 | setInvocations, 68 | setDuration, 69 | setErrors, 70 | setCost, 71 | setThrottles, 72 | timePeriod, 73 | userRegion, 74 | syncData 75 | ); 76 | setSyncData(false); 77 | } 78 | }, [timePeriod, syncData, userRegion]); 79 | 80 | // Initial firing of data request upon Get Started or Login 81 | useEffect(() => { 82 | if (start) { 83 | // console.log('running fetch Metric ALL Functions'); 84 | fetchHelper.fetchMetricAllFunctions( 85 | setFuncNames, 86 | setTotalInvocations, 87 | setTotalErrors, 88 | setTotalCost, 89 | setSlowestFuncs, 90 | setErrorMsgs, 91 | setMostErroredFuncs, 92 | setMemUsedVsAllo, 93 | timePeriod, 94 | userRegion 95 | ); 96 | // console.log('running fetch Metric BY Functions'); 97 | fetchHelper.fetchMetricEachFunctions( 98 | setInvocations, 99 | setDuration, 100 | setErrors, 101 | setCost, 102 | setThrottles, 103 | timePeriod, 104 | userRegion 105 | ); 106 | setSyncData(false); 107 | } 108 | }, [start, timePeriod, userRegion]); 109 | 110 | return ( 111 | 112 | 113 |
114 | 115 | 116 | 117 | ( 121 | 126 | )} 127 | /> 128 | ( 132 | 137 | )} 138 | /> 139 | 140 | {/* DASHBOARD ROUTE */} 141 | 142 | 151 | 152 | ( 156 | 168 | )} 169 | /> 170 | 171 | {/* FUNCTIONS ROUTE */} 172 | ( 176 | 187 | )} 188 | /> 189 | 190 | {/* ALLOCATIONS ROUTE */} 191 | ( 195 | 199 | )} 200 | /> 201 | 202 | 203 | 204 | 205 |
206 |
207 |
208 | ); 209 | }; 210 | 211 | export default App; 212 | -------------------------------------------------------------------------------- /src/animations/BlobGeometry.jsx: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import React, { useEffect, useRef, forwardRef } from 'react' 3 | import { ComputedAttribute } from '@react-three/drei' 4 | import Perlin from 'perlin' 5 | 6 | Perlin.seed(Math.random()) 7 | 8 | const computeFlowerDensity = (geometry) => { 9 | const position = geometry.getAttribute('position') 10 | const density = [] 11 | const vertex = new THREE.Vector3() 12 | for (let i = 0; i < position.count; i++) { 13 | vertex.fromBufferAttribute(position, i) 14 | const p = vertex.clone().multiplyScalar(1) 15 | const n = Perlin.simplex3(...p.toArray()) 16 | let m = THREE.MathUtils.mapLinear(n, -1, 1, 0, 1) 17 | if (m > 0.15) m = 0 18 | density.push(m) 19 | } 20 | return new THREE.Float32BufferAttribute(density, 1) 21 | } 22 | 23 | export const BlobGeometry = forwardRef((props, ref) => { 24 | const geom = useRef() 25 | 26 | useEffect(() => { 27 | const vertex = new THREE.Vector3() 28 | const normal = new THREE.Vector3() 29 | let newPositionAttribute = [] 30 | const positionAttribute = geom.current.getAttribute('position') 31 | const normalAttribute = geom.current.getAttribute('normal') 32 | for (let i = 0; i < positionAttribute.count; i++) { 33 | vertex.fromBufferAttribute(positionAttribute, i) 34 | normal.fromBufferAttribute(normalAttribute, i) 35 | const v = vertex.multiplyScalar(0.5) 36 | const n = Perlin.simplex3(...v.toArray()) 37 | vertex.add(normal.multiplyScalar(n * 0.3)) 38 | newPositionAttribute.push(vertex.x, vertex.y, vertex.z) 39 | } 40 | geom.current.setAttribute('position', new THREE.Float32BufferAttribute(newPositionAttribute, 3)) 41 | geom.current.attributes.position.needsUpdate = true 42 | geom.current.computeVertexNormals() 43 | }, []) 44 | 45 | return ( 46 | 47 | 48 | 49 | 50 | 51 | 52 | ) 53 | }) 54 | -------------------------------------------------------------------------------- /src/animations/Butterfly.jsx: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { useRef, useEffect, useMemo, useState } from 'react' 3 | import { useFrame } from '@react-three/fiber' 4 | import { useGLTF, useAnimations } from '@react-three/drei' 5 | import { SkeletonUtils } from 'three-stdlib' 6 | import { FBM } from 'three-noise' 7 | import React, { useState, useEffect, useMemo, FC } from 'react'; 8 | 9 | const vec = new THREE.Vector2() 10 | export function Butterfly(props) { 11 | const group = useRef() 12 | const { scene, animations } = useGLTF('/models/butterfly.glb') 13 | const cloneScene = useMemo(() => SkeletonUtils.clone(scene), [scene]) 14 | const { actions } = useAnimations(animations, group) 15 | const [fbm] = useState(() => new FBM({ seed: Math.random() })) 16 | const [offset] = useState(() => Math.random() * 100) 17 | 18 | useEffect(() => { 19 | actions['ArmatureAction.001'].setEffectiveTimeScale(6) 20 | const timeout = setTimeout(() => actions['ArmatureAction.001'].play(), Math.random() * 1000) 21 | group.current.rotation.y = offset 22 | return () => { 23 | clearTimeout(timeout) 24 | actions['ArmatureAction.001']?.stop() 25 | } 26 | }, []) 27 | 28 | useFrame(({ clock }, dt) => { 29 | vec.set(clock.elapsedTime, clock.elapsedTime) 30 | group.current.position.set(0, fbm.get2(vec), 0) 31 | group.current.rotation.y -= dt 32 | }) 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | 45 | useGLTF.preload('/scene (3).glb') -------------------------------------------------------------------------------- /src/animations/Flower.jsx: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { forwardRef } from 'react' 3 | import { useGLTF, useTexture } from '@react-three/drei' 4 | import { LayerMaterial, Texture } from 'lamina' 5 | import React, { useState, useEffect, useMemo, FC } from 'react'; 6 | 7 | export const Flower = forwardRef((props, ref) => { 8 | const { nodes } = useGLTF('/models/flower.glb') 9 | const map = useTexture('/models/Sea_Thrift_vkbgaihha/Albedo_2K__vkbgaihha.jpg') 10 | const ao = useTexture('/models/Sea_Thrift_vkbgaihha/AO_2K__vkbgaihha.jpg') 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ) 22 | }) 23 | -------------------------------------------------------------------------------- /src/animations/Grass.jsx: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import React, { cloneElement, useEffect, useRef } from 'react' 3 | import { extend, useFrame } from '@react-three/fiber' 4 | import { Sampler } from '@react-three/drei' 5 | import { Depth, LayerMaterial } from 'lamina' 6 | import Perlin from 'perlin.js' 7 | import WindLayer from './WindLayer' 8 | import { Flower } from './Flower' 9 | 10 | Perlin.seed(Math.random()) 11 | extend({ WindLayer }) 12 | 13 | export function Grass({ children, strands = 50000, ...props }) { 14 | const geomRef = useRef() 15 | const meshRef = useRef(null) 16 | const windLayer = useRef(null) 17 | const flowerRef = useRef() 18 | 19 | useEffect(() => { 20 | meshRef.current.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)) 21 | meshRef.current.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5)) 22 | flowerRef.current.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)) 23 | flowerRef.current.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5)) 24 | }, []) 25 | 26 | useFrame(() => (windLayer.current.time += 0.005)) 27 | 28 | return ( 29 | <> 30 | {cloneElement(children, { ref: geomRef })} 31 | 32 | 33 | 34 | 35 | 45 | 46 | 47 | 48 | 49 | { 51 | const object = transform(props) 52 | const n = Perlin.simplex3(...props.position.clone().multiplyScalar(5).toArray()) 53 | object.scale.setScalar(THREE.MathUtils.mapLinear(n, -1, 1, 0.3, 1) * 0.1) 54 | return object 55 | }} 56 | mesh={geomRef} 57 | instances={meshRef} 58 | /> 59 | 60 | 61 | 62 | ) 63 | } 64 | 65 | const transform = ({ position, normal, dummy: object }) => { 66 | object.scale.setScalar(Math.random() * 0.0075) 67 | object.position.copy(position) 68 | object.lookAt(normal.add(position)) 69 | object.rotation.y += Math.random() - 0.5 * (Math.PI * 0.5) 70 | object.rotation.x += Math.random() - 0.5 * (Math.PI * 0.5) 71 | object.rotation.z += Math.random() - 0.5 * (Math.PI * 0.5) 72 | object.updateMatrix() 73 | return object 74 | } -------------------------------------------------------------------------------- /src/animations/Particles.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as THREE from 'three' 3 | import { useMemo, useRef } from 'react' 4 | import { useFrame } from '@react-three/fiber' 5 | import CustomShaderMaterial from 'three-custom-shader-material' 6 | 7 | export function Particles({ amount = 300, size = 0.01, opacity = 0.5 }) { 8 | const matRef = useRef() 9 | const positions = useMemo(() => new Float32Array(Array.from({ length: amount * 3 }, () => Math.random())), [length]) 10 | useFrame(({ clock }) => (matRef.current.uniforms.u_time.value = clock.elapsedTime)) 11 | return ( 12 | 13 | 14 | 15 | 16 | 33 | 34 | ) 35 | } -------------------------------------------------------------------------------- /src/animations/WindLayer.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import { Abstract } from 'lamina/vanilla' 3 | import React, { useState, useEffect, useMemo, FC } from 'react'; 4 | 5 | export default class WindLayer extends Abstract { 6 | static u_time = 0 7 | static u_sway = 0.5 8 | static u_length = 1 9 | static u_spherePos = new THREE.Vector3(0, 0, 0) 10 | static u_noiseScale = 10.0 11 | static u_noiseStrength = 10.0 12 | static u_colorA = new THREE.Color('#ade266').convertSRGBToLinear() 13 | static u_colorB = new THREE.Color('#ade266').convertSRGBToLinear() 14 | 15 | static vertexShader = ` 16 | uniform float u_time; 17 | uniform float u_sway; 18 | uniform float u_length; 19 | uniform vec3 u_spherePos; 20 | varying vec3 v_pos; 21 | 22 | vec3 main() { 23 | float cover = .25; 24 | vec3 pos = position.xyz; 25 | vec3 base = vec3(pos.x, pos.y, 0.0); 26 | vec4 baseGP = instanceMatrix * vec4(base, 1.0); 27 | v_pos = baseGP.xyz; 28 | vec2 noise = (lamina_noise_curl(baseGP.xyz * vec3(0.1) + u_time * 0.5 * u_sway)).xy; 29 | noise = smoothstep(-1.0, 1.0, noise); 30 | float swingX = sin(u_time * 2.0 + noise.x * 2.0 * PI) * pow(pos.z, 2.0); 31 | float swingY = cos(u_time * 2.0 + noise.y * 2.0 * PI) * pow(pos.z, 2.0); 32 | float d = distance(u_spherePos, baseGP.xyz); 33 | float radius = 0.75; 34 | float intensity = (1. - min(d, radius) / radius) * 0.5; 35 | pos.x += swingX + intensity; 36 | pos.y += swingY + intensity; 37 | return (pos * u_length); 38 | } 39 | ` 40 | 41 | static fragmentShader = ` 42 | varying vec3 v_pos; 43 | uniform float u_noiseScale; 44 | uniform float u_noiseStrength; 45 | uniform vec3 u_colorA; 46 | uniform vec3 u_colorB; 47 | 48 | vec4 main() { 49 | float n = lamina_noise_perlin(v_pos * u_noiseScale) * u_noiseStrength; 50 | vec3 c = mix(u_colorB, u_colorA, n); 51 | return vec4(vec3(c), 1.); 52 | } 53 | ` 54 | 55 | constructor(props) { 56 | super(WindLayer, { 57 | name: 'GrassLayer', 58 | ...props 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/animations/tree.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/src/animations/tree.glb -------------------------------------------------------------------------------- /src/components/BarFnGraph.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import moment from 'moment'; 3 | import { dummyData } from '../../Data/byFunc/dummyData'; 4 | 5 | import { 6 | BarChart, 7 | Bar, 8 | Cell, 9 | XAxis, 10 | YAxis, 11 | CartesianGrid, 12 | Tooltip, 13 | Legend, 14 | LabelList, 15 | ResponsiveContainer, 16 | } from 'recharts'; 17 | 18 | const barColors = ['#850909', '#8e2222', '#d82e2e', '#d25757', '#e728']; 19 | 20 | interface BarFnGraphProps { 21 | data: Record[]; 22 | name: string; 23 | width: string | number; 24 | tooltip: string; 25 | } 26 | 27 | const BarFuncGraph = (props: BarFnGraphProps) => { 28 | return ( 29 | <> 30 |

37 | {props.name} 38 |

39 |
40 | 41 | 52 | 53 | 54 | 61 | 62 | 63 | 69 | {props.data.map((entry, index) => ( 70 | 71 | ))} 72 | 81 | 82 | 83 | 84 |
85 | 86 | ); 87 | }; 88 | 89 | export default BarFuncGraph; 90 | -------------------------------------------------------------------------------- /src/components/ErrorTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { errorMessagesMock } from '.././../Data/byFunc/errorMessagesMock'; 4 | import { 5 | ErrorTableTable, 6 | ErrorTableRow, 7 | ErrorTableCell, 8 | ErrorTableDiv, 9 | } from '../styles'; 10 | 11 | interface ErrorTableProps { 12 | data: any[]; 13 | } 14 | 15 | interface LogInfo { 16 | funcName: string; 17 | id: string | number; 18 | date: string; 19 | message: string; 20 | } 21 | 22 | //MOVE TO STYLES PAGE ONCE DONE 23 | const Table = styled.table` 24 | width: 100%; 25 | background: white; 26 | border: 1px solid black; 27 | box-sizing: border-box; 28 | border-collapse: collapse; 29 | color: black; 30 | `; 31 | // how do I get it to scroll for overflow? 32 | // overflow: scroll; 33 | 34 | //WHEN FETECHING, REMEMBER TO ADD props:ErrorProps 35 | const ErrorTable = (props: ErrorTableProps) => { 36 | //create result array with modified data (may need to move into backend) 37 | const dataArray: { [key: string]: string | number }[] = []; 38 | let numLogs = 0; 39 | if (props.data) 40 | //iterate through array of objects 41 | for (let i = 0; i < props.data.length; i++) { 42 | for (let j = 0; j < props.data[i]['logs'].length; j++) { 43 | //take function, and then individual logs 44 | const logInfo: { [key: string]: string | number } = {}; 45 | logInfo['funcName'] = props.data[i]['function']; 46 | logInfo['id'] = props.data[i]['logs'][j]['id']; 47 | logInfo['date'] = props.data[i]['logs'][j]['date']; 48 | logInfo['message'] = props.data[i]['logs'][j]['message']; 49 | dataArray.push(logInfo); 50 | numLogs++; 51 | } 52 | } 53 | if (numLogs === 0) { 54 | const logInfo: { [key: string]: string | number } = {}; 55 | logInfo['funcName'] = 'No error logs recently'; 56 | logInfo['id'] = ''; 57 | logInfo['date'] = ''; 58 | logInfo['message'] = ''; 59 | dataArray.push(logInfo); 60 | } 61 | //Ideal result: 62 | // [...{ 63 | // funcName: functionName 64 | // id: id 65 | // date: date here 66 | // message: message 67 | // }] 68 | 69 | //iterate through the data array to create divs 70 | const errorDivs = []; 71 | for (let i = 0; i < dataArray.length; i++) { 72 | errorDivs.push( 73 | 74 | {dataArray[i].funcName} 75 | {dataArray[i].id} 76 | {dataArray[i].date} 77 | {dataArray[i].message} 78 | 79 | ); 80 | } 81 | 82 | return ( 83 | <> 84 |

91 | Error Logs 92 |

93 | 94 | 95 | 96 | 97 | Function 98 | ID 99 | Date 100 | Message 101 | 102 | 103 | {errorDivs} 104 | 105 | 106 | 107 | ); 108 | }; 109 | 110 | export default ErrorTable; 111 | -------------------------------------------------------------------------------- /src/components/FnGraph.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import moment from 'moment'; 3 | 4 | import { 5 | LineChart, 6 | Line, 7 | XAxis, 8 | YAxis, 9 | CartesianGrid, 10 | Tooltip, 11 | Legend, 12 | ResponsiveContainer, 13 | } from 'recharts'; 14 | 15 | interface FnGraphProps { 16 | tooltip: string; 17 | data: object[]; 18 | name: string; 19 | width: string | number; 20 | unit?: string; 21 | } 22 | 23 | const FnGraph = (props: FnGraphProps) => { 24 | return ( 25 | <> 26 |

33 | {props.name} 34 |

35 |
36 | 37 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 72 | 73 | 74 |
75 | 76 | ); 77 | }; 78 | 79 | export default FnGraph; 80 | -------------------------------------------------------------------------------- /src/components/FnGraphCompare.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import moment from 'moment'; 3 | // import { dummyData } from '../../Data/byFunc/dummyData'; 4 | 5 | import { 6 | LineChart, 7 | Line, 8 | XAxis, 9 | YAxis, 10 | CartesianGrid, 11 | Tooltip, 12 | Legend, 13 | ResponsiveContainer, 14 | } from 'recharts'; 15 | 16 | interface FnGraphCompareProps { 17 | onFunctions: Record; 18 | name: string; 19 | width: string | number; 20 | data: any[]; 21 | unit?: string; 22 | } 23 | 24 | const FuncGraphCompare = (props: FnGraphCompareProps) => { 25 | const lines = []; 26 | for (const func in props.onFunctions) { 27 | lines.push( 28 | 38 | ); 39 | } 40 | 41 | return ( 42 | <> 43 |

50 | {props.name} 51 |

52 |
53 | 54 | 64 | 65 | 66 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {lines} 79 | 80 | 81 |
82 | 83 | ); 84 | }; 85 | 86 | export default FuncGraphCompare; 87 | -------------------------------------------------------------------------------- /src/components/FnSelector.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { FnSelectButton, FnSideBarWrapper } from '../styles'; 4 | 5 | //types expected for FunctionSelector 6 | interface IFunctionSelector { 7 | funcNames: string[]; 8 | onFunctions: Record; 9 | setOnFunctions: Function; 10 | } 11 | 12 | const FunctionSelector = (props: IFunctionSelector) => { 13 | const colors = ['red', 'blue', 'orange', 'green', 'purple']; 14 | const [ colorArr, setColorArr ] = useState(colors) 15 | let [counter, setCounter] = useState(0); 16 | 17 | const handleClick = (e: React.MouseEvent) => { 18 | const functionClicked = (e.target as HTMLElement).id; 19 | const newState = { ...props.onFunctions }; 20 | const newColor = '#' + Math.floor(Math.random()*16777215).toString(16) 21 | colorArr.push(newColor); 22 | colorArr.shift(); 23 | setColorArr(colorArr); 24 | if (props.onFunctions.hasOwnProperty(functionClicked)) { 25 | // Function button already clicked 26 | delete newState[functionClicked]; 27 | props.setOnFunctions(newState); 28 | (e.target as HTMLElement).setAttribute( 29 | 'style', 30 | 'background-color: #a674c1' 31 | ); 32 | } else { 33 | // Function button not yet clicked 34 | // Make new property on onFunctions 35 | newState[functionClicked] = colorArr[counter]; 36 | setCounter(counter + 1); 37 | if(counter > 3) setCounter(0); 38 | console.log(colorArr) 39 | console.log(`color array at ${counter} is ${colorArr[counter]}`) 40 | props.setOnFunctions(newState); 41 | (e.target as HTMLElement).setAttribute( 42 | 'style', 43 | `background-color: ${colorArr[counter]}` 44 | ); 45 | } 46 | }; 47 | 48 | const functionButtons = []; 49 | for (let i = 0; i < props.funcNames.length; i++) { 50 | functionButtons.push( 51 | 56 | {props.funcNames[i]} 57 | 58 | ); 59 | } 60 | 61 | return ( 62 | <> 63 | {/* */} 64 |

Functions

65 |
{functionButtons}
66 | {/*
*/} 67 | 68 | ); 69 | }; 70 | 71 | export default FunctionSelector; 72 | -------------------------------------------------------------------------------- /src/components/MemFnSelector.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { FnSelectButton } from '../styles'; 3 | interface Props { 4 | names: string[]; 5 | onStacked: {[key:string]: string | number}[]; 6 | setOnStacked: Function; 7 | data: object[]; 8 | } 9 | 10 | const AlloFunctionSelector = (props: Props) => { 11 | const colors = ['red', 'blue', 'orange', 'green', 'purple']; 12 | const [ colorArr, setColorArr ] = useState(colors) 13 | const [counter, setCounter] = useState(0); 14 | 15 | const handleClick = (e: React.MouseEvent) => { 16 | const functionClicked = (e.target as HTMLElement).id; 17 | 18 | // 2 level shallow clone of current state 19 | const newState: {[key:string]: string | number}[] = []; 20 | props.onStacked.map((func: {[key:string]: string | number}) => { 21 | newState.push({ ...func }); 22 | }); 23 | 24 | // Helper Function: Does onStacked hook have clicked function? 25 | const hasFunction = (functionName: string, newState: {[key:string]: string | number}[]): boolean => { 26 | for (let i = 0; i < newState.length; i++) { 27 | if (newState[i].name === functionName) return true; 28 | } 29 | return false; 30 | }; 31 | 32 | // Helper Function: Delete clicked function entry from onStacked hook 33 | const deleteFunction = (functionName: string, newState: {[key:string]: string | number}[]): void => { 34 | for (let i = 0; i < newState.length; i++) { 35 | if (newState[i].name === functionName) { 36 | for (let j = i; j < newState.length; j++) { 37 | newState[i] = newState[i + 1]; 38 | } 39 | newState.pop(); 40 | if (newState.length === 0) 41 | newState.push({ name: 'Select a function' }); 42 | return; 43 | } 44 | } 45 | }; 46 | 47 | // Helper Function: Add clicked function entry from fetched data into onStacked hook 48 | const addFunction = ( 49 | functionName: string, 50 | data: any[], 51 | newState: any[] 52 | ): void => { 53 | if (newState[0].name === 'Select a function') newState.pop(); 54 | for (let i = 0; i < data.length; i++) { 55 | if (data[i].name === functionName) { 56 | newState.push({ ...data[i], color: colorArr[counter] }); 57 | return; 58 | } 59 | } 60 | }; 61 | 62 | const newColor = '#' + Math.floor(Math.random()*16777215).toString(16) 63 | colorArr.push(newColor); 64 | colorArr.shift(); 65 | setColorArr(colorArr); 66 | 67 | if (hasFunction(functionClicked, newState)) { 68 | // Function button already clicked 69 | deleteFunction(functionClicked, newState); 70 | props.setOnStacked(newState); 71 | (e.target as HTMLElement).setAttribute( 72 | 'style', 73 | 'background-color: #a674c1' 74 | ); 75 | } else { 76 | // Function button not yet clicked 77 | // Make new property on onFunctions 78 | addFunction(functionClicked, props.data, newState); 79 | setCounter(counter + 1); 80 | if(counter > 3) setCounter(0); 81 | props.setOnStacked(newState); 82 | (e.target as HTMLElement).setAttribute( 83 | 'style', 84 | `background-color: ${colorArr[counter]}` 85 | ); 86 | } 87 | }; 88 | 89 | const functionButtons = []; 90 | for (let i = 0; i < props.names.length; i++) { 91 | functionButtons.push( 92 | 97 | {props.names[i]} 98 | 99 | ); 100 | } 101 | 102 | return ( 103 | <> 104 |

Function

105 |
{functionButtons}
106 | 107 | ); 108 | }; 109 | 110 | export default AlloFunctionSelector; 111 | -------------------------------------------------------------------------------- /src/components/MemReduction.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { memUsedVsAllo } from '../../Data/byFunc/memUsedVsAllo'; 3 | import { MemUsed } from '../../server/types'; 4 | 5 | // const fetchedData = memUsedVsAllo; 6 | 7 | interface IMemReduction{ 8 | memUsedVsAllo: {[key: string]: string | number}[] 9 | } 10 | 11 | const MemReduction = (props:IMemReduction) => { 12 | //declare var to hold all divs 13 | // const fetchedData = props.memUsedVsAllo 14 | const eligibleFuncs = []; 15 | const memRatioOrdered = []; 16 | 17 | for (let i = 0; i < props.memUsedVsAllo.length; i++) { 18 | const objOfInterest = props.memUsedVsAllo[i]; 19 | const funcName = objOfInterest.name; 20 | const diff: number = Number(objOfInterest[`diff${funcName}`]); 21 | const allocated: number = Number(props.memUsedVsAllo[i][`allo${funcName}`]); 22 | const memRatio = Math.floor((diff / allocated) * 100); 23 | if (memRatio > 80) { 24 | memRatioOrdered.push([funcName, memRatio]); 25 | } 26 | } 27 | memRatioOrdered.sort((a, b) => { 28 | if (a[1] < b[1]) return 1; 29 | if (a[1] > b[1]) return -1; 30 | else return 0; 31 | }); 32 | 33 | for (let i = 0; i < memRatioOrdered.length; i++) { 34 | eligibleFuncs.push( 35 |
  • 36 | Function {memRatioOrdered[i][0]} qualifies for lower memory allocation. 37 |
    38 | There is {memRatioOrdered[i][1]}% of free memory space. 39 |
  • 40 | ); 41 | } 42 | 43 | return <>{eligibleFuncs}; 44 | }; 45 | 46 | export default MemReduction; 47 | -------------------------------------------------------------------------------- /src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import { UserContext } from '../../context/userContext'; 3 | import { useEffect } from 'react'; 4 | import { useHistory } from 'react-router-dom'; 5 | import { BasicBtn, LogoutBtn, SideAct, MainNav, SpinBtn, SelectorBox, RegionSelect } from '../styles'; 6 | import { library } from '@fortawesome/fontawesome-svg-core'; 7 | import { fas } from '@fortawesome/free-solid-svg-icons'; 8 | 9 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 10 | // import { fa-solid, fa-chart-line } from '@fortawesome/free-solid-svg-icons'; 11 | 12 | import { 13 | IconLookup, 14 | IconDefinition, 15 | findIconDefinition, 16 | } from '@fortawesome/fontawesome-svg-core'; 17 | 18 | library.add(fas); 19 | 20 | const gearLookup: IconLookup = { prefix: 'fas', iconName: 'gear' }; 21 | const gearIconDefinition: IconDefinition = findIconDefinition(gearLookup); 22 | 23 | const RotateLookup: IconLookup = { prefix: 'fas', iconName: 'rotate' }; 24 | const RotateIconDefinition: IconDefinition = findIconDefinition(RotateLookup); 25 | 26 | const CloudLookup: IconLookup = { prefix: 'fas', iconName: 'cloud' }; 27 | const CloudIconDefinition: IconDefinition = findIconDefinition(CloudLookup); 28 | 29 | // coffeeIconDefinition 30 | 31 | type Props = { 32 | currentView: string; 33 | setCurrentView: Function; 34 | setSyncData: Function; 35 | setStart: Function; 36 | setUserRegion: Function; 37 | }; 38 | 39 | const Sidebar = (props: Props) => { 40 | const { name } = useContext(UserContext) 41 | 42 | let history = useHistory(); 43 | 44 | const dashBtnHandler = (event: React.MouseEvent) => { 45 | event.preventDefault(); 46 | props.setCurrentView('dashboard'); 47 | history.push('/home'); 48 | }; 49 | 50 | const funcBtnHandler = (event: React.MouseEvent) => { 51 | event.preventDefault(); 52 | props.setCurrentView('functions'); 53 | history.push('/functions'); 54 | }; 55 | 56 | const alloBtnHandler = (event: React.MouseEvent) => { 57 | event.preventDefault(); 58 | props.setCurrentView('memory'); 59 | history.push('/memory'); 60 | }; 61 | 62 | //Button to trigger fetching of data from AWS Cloudwatch 63 | const syncBtnHandler = (event: React.MouseEvent) => { 64 | // console.log('sync button clicked'); 65 | props.setSyncData(true); 66 | }; 67 | 68 | const logOutHandler = async () => { 69 | // console.log('log out clicked!'); 70 | //post request to /signout 71 | const leaving = await fetch('/api/user/signout', { 72 | headers: { 73 | 'Content-Type': 'application/json', 74 | }, 75 | credentials: 'include', 76 | method: 'POST', 77 | }); 78 | // console.log(leaving); 79 | 80 | props.setCurrentView('login'); 81 | props.setStart(false); 82 | history.push('/login'); 83 | }; 84 | 85 | 86 | const updateRegion = (e: React.ChangeEvent): void =>{ 87 | props.setUserRegion(e.target.value); 88 | } 89 | 90 | 91 | return ( 92 | <> 93 | 94 |
  • 95 | 96 |
  • 97 |
  • accumulus
  • 98 |
  • 99 | 108 | Dashboard 109 | 110 |
  • 111 |
  • 112 | 121 | Functions 122 | 123 |
  • 124 |
  • 125 | 134 | Memory 135 | 136 |
  • 137 |
  • Welcome{`${name}`}
  • 138 |
  • 139 |
  • 140 |
      141 | {/* */} 142 | {/* */} 143 | {/* */} 149 | {/* */} 150 | {/* */} 151 |
    152 |
  • 153 |
  • Log Out
  • 154 |
    155 | 156 | ); 157 | }; 158 | // 159 | export default Sidebar; 160 | -------------------------------------------------------------------------------- /src/components/StackedBarFnGraph.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import moment from 'moment'; 3 | 4 | import { 5 | BarChart, 6 | Bar, 7 | XAxis, 8 | YAxis, 9 | CartesianGrid, 10 | Tooltip, 11 | Legend, 12 | ResponsiveContainer, 13 | } from 'recharts'; 14 | 15 | interface StackedBarFnGraphProps { 16 | onStacked: any[]; 17 | name: string; 18 | width: number | string; 19 | height: number | string; 20 | } 21 | 22 | const StackedBarFuncGraph = (props: StackedBarFnGraphProps) => { 23 | const bars: any[] = []; 24 | 25 | props.onStacked.map((el) => { 26 | bars.push( 27 | //
    28 | <> 29 | 36 | 43 | 44 | //
    45 | ); 46 | }); 47 | 48 | return ( 49 | <> 50 |

    57 | {props.name} 58 |

    59 |
    63 | 64 | 74 | 75 | 76 | 77 | 78 | 79 | {bars} 80 | 81 | 82 |
    83 | 84 | ); 85 | }; 86 | 87 | export default StackedBarFuncGraph; 88 | -------------------------------------------------------------------------------- /src/components/TimeButtons.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { FnSelectButton, BasicBtn } from '../styles'; 3 | 4 | type Props = { 5 | timePeriod: string 6 | setTimePeriod: Function; 7 | }; 8 | 9 | const TimeButtons = (props: Props) =>{ 10 | 11 | const changeToHour = () =>{ 12 | props.setTimePeriod('1hr'); 13 | } 14 | 15 | const changeToHalfDay = () =>{ 16 | props.setTimePeriod('12hr'); 17 | } 18 | 19 | const changeToFullDay = () =>{ 20 | props.setTimePeriod('24hr'); 21 | } 22 | 23 | const changeToWeek = () => { 24 | props.setTimePeriod('7d'); 25 | } 26 | 27 | return ( 28 | <> 29 |

    Time

    30 | 34 | 1 Hour 35 | 36 | 40 | 12 Hour 41 | 42 | 46 | 24 Hour 47 | 48 | 52 | 1 week 53 | 54 | 55 | ); 56 | } 57 | 58 | export default TimeButtons; -------------------------------------------------------------------------------- /src/components/login-btn.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function LoginBtn() { 4 | return ( 5 | <> 6 |
    7 | 8 |
    9 | 10 | ); 11 | } 12 | 13 | export default LoginBtn; 14 | -------------------------------------------------------------------------------- /src/components/signup-btn.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function SignupBtn() { 4 | return ( 5 | <> 6 |
    7 | 8 |
    9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/splash-menu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import LoginButton from './login-btn'; 4 | import SignupButton from './signup-btn'; 5 | 6 | export default function Menu() { 7 | return ( 8 | <> 9 |
    10 |

    Accumulus Home Page

    11 | 12 | 13 |
    14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/globals.tsx: -------------------------------------------------------------------------------- 1 | import {createGlobalStyle} from "styled-components" 2 | 3 | const GlobalStyle = createGlobalStyle` 4 | *{ 5 | /* margin: 0; */ 6 | margin-top: 0; 7 | margin-left: 0; 8 | margin-right: 0; 9 | margin-bottom: 0; 10 | /* padding: 0; */ 11 | outline:0; 12 | box-sizing:border-box; 13 | font-family: 'Quicksand', sans-serif; 14 | } 15 | #root{ 16 | margin:0 auto; 17 | } 18 | ` 19 | 20 | export default GlobalStyle; -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Accumulus 8 | 9 | 10 | 11 | 12 |
    13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const container = document.getElementById('root'); 6 | const root = createRoot(container as HTMLElement); 7 | 8 | root.render(); 9 | -------------------------------------------------------------------------------- /src/pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { UserContext } from '../../context/userContext'; 3 | import FnGraph from '../components/FnGraph'; 4 | import BarFnGraph from '../components/BarFnGraph'; 5 | import ErrorTable from '../components/ErrorTable'; 6 | import TimeButtons from '../components/TimeButtons'; 7 | import { 8 | SideBarDiv, 9 | GraphContainer, 10 | DashboardGrid, 11 | Row1GraphBox, 12 | Row2GraphBox, 13 | EvenDashGraphBox, 14 | DashSideWrapper, 15 | SelectorBox, 16 | RegionSelect, 17 | } from '../styles'; 18 | 19 | type Props = { 20 | setCurrentView: Function; 21 | totalInvocations: object[]; 22 | totalErrors: object[]; 23 | totalCost: object[]; 24 | slowestFuncs: object[]; 25 | errorMsgs: object[]; 26 | mostErroredFuncs: object[]; 27 | timePeriod: string; 28 | setTimePeriod: Function; 29 | setUserRegion: Function; 30 | }; 31 | 32 | const Dashboard = (props: Props, { setCurrentView, setUserRegion }: Props) => { 33 | const { name, storeName, email, storeEmail } = useContext(UserContext); 34 | 35 | const updateRegion = (e: React.ChangeEvent): void => { 36 | props.setUserRegion(e.target.value); 37 | }; 38 | 39 | return ( 40 | <> 41 | 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 115 | 116 | 117 | 118 | 119 | ); 120 | }; 121 | 122 | export default Dashboard; 123 | -------------------------------------------------------------------------------- /src/pages/Functions.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import FnGraphCompare from '../components/FnGraphCompare'; 3 | import FnSelector from '../components/FnSelector'; 4 | import TimeButtons from '../components/TimeButtons'; 5 | import { 6 | SideBarDiv, 7 | FnGraphContainer, 8 | FnGrid, 9 | Scroll, 10 | FnSideBarWrapper, 11 | } from '../styles'; 12 | 13 | type Props = { 14 | setCurrentView: Function; 15 | funcNames: string[]; 16 | invocations: object[]; 17 | duration: object[]; 18 | errors: object[]; 19 | cost: object[]; 20 | throttles: object[]; 21 | timePeriod: string; 22 | setTimePeriod: Function; 23 | }; 24 | 25 | const Functions = (props: Props, { setCurrentView }: Props) => { 26 | const [onFunctions, setOnFunctions] = useState({}); 27 | 28 | return ( 29 | <> 30 | 31 | 32 | 33 | 38 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | 54 | 60 | 61 | 62 | 68 | 69 | 70 | 77 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | ); 90 | }; 91 | 92 | export default Functions; 93 | -------------------------------------------------------------------------------- /src/pages/Login.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import Register from './Register'; 3 | import { UserContext } from '../../context/userContext'; 4 | import { useHistory } from 'react-router-dom'; 5 | import { 6 | LoginPageContainer, 7 | LoginFormContainer, 8 | LoginButton, 9 | LoginInput, 10 | ErrorMessage, 11 | BackButton, 12 | } from '../styles'; 13 | import { useForm } from 'react-hook-form'; 14 | 15 | type FormData = { 16 | email: string; 17 | password: string; 18 | }; 19 | 20 | type Props = { 21 | setCurrentView: Function; 22 | setUserRegion: Function; 23 | setStart: Function; 24 | }; 25 | 26 | const Login = ({ setCurrentView, setUserRegion, setStart }: Props) => { 27 | const { name, storeName, email, storeEmail } = useContext(UserContext); 28 | 29 | const { 30 | register, 31 | handleSubmit, 32 | formState: { errors }, 33 | } = useForm(); 34 | 35 | const [emailLog, setEmailLog] = useState(''); 36 | const [passLog, setPassLog] = useState(''); 37 | const [loginOrRegister, setLoginOrRegister] = useState('login'); 38 | const [message, setMessage] = useState(''); 39 | 40 | let history = useHistory(); 41 | 42 | const onSubmit = async (data: FormData) => { 43 | const body = JSON.stringify({ 44 | email: emailLog, 45 | password: passLog, 46 | }); 47 | 48 | const login = await fetch('/api/user/login', { 49 | headers: { 50 | 'Content-Type': 'application/json', 51 | }, 52 | credentials: 'include', 53 | method: 'POST', 54 | body, 55 | }); 56 | 57 | const response = await login.json(); 58 | const region = response.region; 59 | const name = response.name; 60 | 61 | if (response.success === true) { 62 | storeName(name); 63 | setUserRegion(region); 64 | setCurrentView('dashboard'); 65 | setStart(true); 66 | history.push('/home'); 67 | } else { 68 | setMessage('Email not registered'); // todo: render something to show incorrect email/password 69 | } 70 | }; 71 | 72 | const regBtnHandler = () => { 73 | setLoginOrRegister('register'); 74 | history.push('/register'); // check if this sends user back to login with browser's back button 75 | }; 76 | 77 | const backBtnHandler = () => { 78 | setCurrentView('splash'); 79 | history.push('/'); 80 | }; 81 | 82 | return ( 83 | <> 84 | 85 | {loginOrRegister === 'login' ? ( 86 | 87 | <> 88 |
    89 |

    Sign In

    90 |
    91 | 92 |
    93 |
    94 | {/* */} 95 |
    96 |
    97 | ()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 103 | })} 104 | type="text" 105 | onChange={(e) => { 106 | setEmailLog(e.target.value); 107 | }} 108 | /> 109 | 110 | {errors.email && ( 111 |
    112 | Enter a valid email address 113 |
    114 | )} 115 |
    116 |
    117 |
    118 |
    119 | {/* */} 120 |
    121 |
    122 | { 127 | setPassLog(e.target.value); 128 | }} 129 | /> 130 | 131 | {errors.password && ( 132 |
    Enter your password
    133 | )} 134 |
    135 |
    136 |
    137 | 138 |
    139 | Log In 140 |
    141 |
    142 | 143 | Don't have an account? 144 | 145 | Back 146 |
    147 |
    148 | 149 |
    150 | ) : ( 151 | 158 | )} 159 |
    160 | 161 | ); 162 | }; 163 | 164 | export default Login; 165 | -------------------------------------------------------------------------------- /src/pages/Memory.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import StackedBarFnGraph from '../components/StackedBarFnGraph'; 3 | import AlloFunctionSelector from '../components/MemFnSelector'; 4 | import MemReduction from '../components/MemReduction'; 5 | import Sidebar from '../components/Navbar'; 6 | import { 7 | SideBarDiv, 8 | FnGrid, 9 | MemoryGrid, 10 | FnGraphContainer, 11 | FnSideBarWrapper, 12 | MemoryReductionContainer 13 | } from '../styles'; 14 | 15 | type Props = { 16 | setCurrentView: Function; 17 | memUsedVsAllo: {[key: string]: string | number}[]; 18 | }; 19 | 20 | const Allocations = ({ setCurrentView, memUsedVsAllo }: Props) => { 21 | const [onStacked, setOnStacked] = useState([{ name: 'Select a function' }]); 22 | 23 | const functions:{[key: string]: string | string[]} = {}; 24 | const names:string[] = []; 25 | for(let i = 0; i < memUsedVsAllo.length; i++){ 26 | names.push(String(memUsedVsAllo[i]['name'])) 27 | } 28 | functions['names'] = names 29 | 30 | return ( 31 | <> 32 | 33 | 34 | 35 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | ); 57 | }; 58 | 59 | export default Allocations; 60 | -------------------------------------------------------------------------------- /src/pages/Register.tsx: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | import React, { useState, useEffect, useContext } from 'react'; 3 | import { UserContext } from '../../context/userContext'; 4 | import { useHistory } from 'react-router-dom'; 5 | import { 6 | RegButton, 7 | RegInput, 8 | RegFormContainer, 9 | ErrorMessage, 10 | BackButton, 11 | } from '../styles'; 12 | import { useForm } from 'react-hook-form'; 13 | 14 | type FormData = { 15 | name: string; 16 | email: string; 17 | password: string; 18 | arn: string; 19 | region: string; 20 | externalId: string; 21 | }; 22 | 23 | const Register = (props: any) => { 24 | const { name, storeName } = useContext(UserContext); 25 | 26 | const { 27 | register, 28 | setValue, 29 | handleSubmit, 30 | formState: { errors }, 31 | } = useForm(); 32 | 33 | const [nameReg, setNameReg] = useState(''); 34 | const [emailReg, setEmailReg] = useState(''); 35 | const [passwordReg, setPasswordReg] = useState(''); 36 | const [arnReg, setArnReg] = useState(''); 37 | const [regionReg, setRegionReg] = useState('us-east-2'); 38 | const [EXTERNAL_ID, setexternelid] = useState(uuidv4()); 39 | 40 | let history = useHistory(); 41 | 42 | useEffect(() => { 43 | setexternelid(uuidv4()); 44 | }, []); 45 | 46 | const YML = `https://accumulus.s3.us-east-2.amazonaws.com/cloudformation.yml`; 47 | 48 | const onSubmit = async (data: FormData) => { 49 | storeName(nameReg); 50 | 51 | const body = JSON.stringify({ 52 | name: nameReg, 53 | email: emailReg, 54 | password: passwordReg, 55 | arn: arnReg, 56 | region: regionReg, 57 | externalId: EXTERNAL_ID, 58 | }); 59 | 60 | const register = await fetch('/api/user/signup', { 61 | headers: { 62 | 'Content-Type': 'application/json', 63 | }, 64 | method: 'POST', 65 | body, 66 | }); 67 | 68 | const response = await register.json(); 69 | const region = response.region; 70 | 71 | if (register.status === 200) { 72 | props.setUserRegion(region); 73 | console.log('redirecting...'); 74 | props.setStart(true); 75 | props.setCurrentView('dashboard'); 76 | history.push('/home'); 77 | } else { 78 | console.log('unsuccessful'); 79 | } 80 | }; 81 | 82 | return ( 83 | <> 84 | 85 |

    Sign Up

    86 |
    87 |
    88 |
    89 |
    90 | { 94 | setNameReg(e.target.value); 95 | }} 96 | /> 97 | 98 | {errors.name &&
    Enter your name
    } 99 |
    100 |
    101 |

    102 |
    103 | ()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 109 | })} 110 | type="text" 111 | onChange={(e) => { 112 | setEmailReg(e.target.value); 113 | }} 114 | /> 115 | 116 | {errors.email && ( 117 |
    Enter a valid email address
    118 | )} 119 |
    120 |
    121 |

    122 |
    123 | { 128 | setPasswordReg(e.target.value); 129 | }} 130 | /> 131 | 132 | {errors.password && ( 133 |
    Enter your password
    134 | )} 135 |
    136 |
    137 |
    138 |
    139 |
    140 | Connect your AWS account to Accumulus by following the steps below: 141 |
    142 |
      143 |
    • 144 | 149 | Add Accumulus CloudFormation stack to AWS. 150 | 151 |
    • 152 |
    • 153 | Make sure you check "I acknowledge that AWS CloudFormation 154 | might create IAM resource." 155 |
    • 156 |
    • Click "Create"
    • 157 |
    • 158 | Once stack create has completed, head to the "Outputs" 159 | tab and look for your "ARN" string. Copy the 160 | "ARN" and paste into the field below. 161 |
    • 162 |
    163 |
    164 |
    165 | 166 |
    167 | { 171 | setArnReg(e.target.value); 172 | }} 173 | /> 174 | 175 | {errors.arn &&
    Enter your ARN
    } 176 |
    177 |
    178 |
    179 |
    180 | 181 |
    182 | 197 |
    198 |
    199 | Sign me up! 200 |
    201 |
    202 | props.setLoginOrRegister('login')} 205 | > 206 | Already have an account? 207 | 208 | Back 209 |
    210 |
    211 |
    212 | 213 | ); 214 | }; 215 | 216 | export default Register; 217 | -------------------------------------------------------------------------------- /src/pages/Splash.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, Suspense } from 'react'; 2 | import { UserContext } from '../../context/userContext'; 3 | import { useHistory } from 'react-router-dom'; 4 | import { 5 | SplashFooter, 6 | SplashLeft, 7 | SplashBody, 8 | StartedButton, 9 | DemoButton, 10 | H1, 11 | Text, 12 | } from '../styles'; 13 | import { Canvas, useFrame, useThree } from '@react-three/fiber'; 14 | import { 15 | Environment, 16 | OrbitControls, 17 | Sky, 18 | Cloud, 19 | useGLTF, 20 | } from '@react-three/drei'; 21 | 22 | type Props = { 23 | setUserRegion: Function; 24 | setCurrentView: Function; 25 | setStart: Function; 26 | }; 27 | 28 | const Splash = ({ setCurrentView, setUserRegion, setStart }: Props) => { 29 | const { name, storeName, email, storeEmail } = useContext(UserContext); 30 | let history = useHistory(); 31 | 32 | const startHandler = async (event: React.MouseEvent) => { 33 | const cookieCheck = await fetch('/api/user/checkCoookies', { 34 | headers: { 35 | 'Content-Type': 'application/json', 36 | }, 37 | credentials: 'include', 38 | method: 'GET', 39 | }); 40 | const response = await cookieCheck.json(); 41 | if ( 42 | response.name && 43 | response.arn && 44 | response.externalId && 45 | response.region 46 | ) { 47 | setUserRegion(response.region); 48 | storeName(response.name); 49 | setCurrentView('dashboard'); 50 | setStart(true); 51 | history.push('/home'); 52 | } else { 53 | setCurrentView('login'); 54 | history.push('/login'); 55 | } 56 | }; 57 | 58 | const demoHandler = async (event: React.MouseEvent) => { 59 | const body = JSON.stringify({ 60 | email: 'email@gmail.com', 61 | password: 'password', 62 | }); 63 | 64 | const login = await fetch('/api/user/login', { 65 | headers: { 66 | 'Content-Type': 'application/json', 67 | }, 68 | credentials: 'include', 69 | method: 'POST', 70 | body, 71 | }); 72 | const response = await login.json(); 73 | const { region, name } = response; 74 | 75 | if (response.success === true) { 76 | storeName(name); 77 | setUserRegion(region); 78 | console.log('redirecting...'); 79 | setCurrentView('dashboard'); 80 | setStart(true); 81 | history.push('/home'); 82 | } else { 83 | console.log('unsucessful'); 84 | } 85 | }; 86 | 87 | function Model(props: any) { 88 | const { scene } = useGLTF('/tree.glb'); 89 | return ; 90 | } 91 | 92 | function Rig() { 93 | const camera = useThree((state) => state.camera); 94 | // state.clock.elapsedTime = 95 | return useFrame((state) => { 96 | camera.position.z = Math.sin(state.clock.elapsedTime) * 10; 97 | // camera.position.x = Math.sin(state.clock.elapsedTime) * -10 98 | 99 | // camera.position.z = Math.sin(100) * 10 100 | }); 101 | } 102 | 103 | return ( 104 | <> 105 | 106 |

    Lambda Monitoring Made Easy

    107 | Get Started 108 | Demo 109 | 110 | Accumulus is an open source application for AWS Lambda data 111 | visualization and cost optimization 112 | 113 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
    141 | 142 | 148 | 149 | 150 | ); 151 | }; 152 | 153 | export default Splash; 154 | -------------------------------------------------------------------------------- /src/public/dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/src/public/dashboard.gif -------------------------------------------------------------------------------- /src/public/functions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/src/public/functions.gif -------------------------------------------------------------------------------- /src/public/memory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/src/public/memory.gif -------------------------------------------------------------------------------- /src/public/sync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Accumulus/04e5975a327dc17dcea82768ebad81c165a5bfe4/src/public/sync.gif -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | // This configuration tells TypeScript to exclude files that look like tests. 2 | // It has to be separate otherwise ts-jest won't see your test files 3 | 4 | { 5 | "extends": "./tsconfig", 6 | "exclude": ["**/*.test.*", "**/__mocks__/*", "**/__tests__/*"] 7 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "strict": true, 6 | "noImplicitReturns": true, 7 | "noImplicitAny": true, 8 | "module": "es2020", 9 | "moduleResolution": "node", 10 | "target": "es6", 11 | "allowJs": true, 12 | "jsx": "react", 13 | "allowSyntheticDefaultImports": true, 14 | "esModuleInterop": true, 15 | "types": ["node", "jest"], 16 | "lib": ["es6", "dom"], 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "skipLibCheck": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "incremental": true, 22 | "noEmit": false 23 | }, 24 | "include": ["./server", "./src"], 25 | "exclude": ["node_modules", "./__tests__", "./dist"] 26 | } 27 | -------------------------------------------------------------------------------- /webpack.config.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './src/index.tsx', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'bundle.js', 9 | }, 10 | plugins: [new HtmlWebpackPlugin({ template: '/src/index.html' })], 11 | devServer: { 12 | port: 8080, 13 | historyApiFallback: true, 14 | static: { 15 | directory: path.resolve(__dirname, '/dist'), 16 | publicPath: '/', 17 | }, 18 | compress: true, 19 | headers: { 'Access-Control-Allow-Origin': '*' }, 20 | proxy: { 21 | '/api/*': "http://localhost:3000" 22 | } 23 | }, 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.m?js$/, 28 | enforce: 'pre', 29 | use: ['source-map-loader'], 30 | }, 31 | { 32 | test: /\.jsx?/, 33 | exclude: /node_modules/, 34 | loader: 'babel-loader', 35 | options: { 36 | presets: ['@babel/preset-env', '@babel/preset-react'], 37 | }, 38 | }, 39 | { 40 | test: /\.s?[ac]ss$/i, 41 | exclude: /node_modules/, 42 | use: ['style-loader', 'css-loader', 'sass-loader'], 43 | }, 44 | // { 45 | // test: /\.tsx?$/, 46 | // exclude: /node_modules/, 47 | // use: 'ts-loader', 48 | // }, 49 | { 50 | test: /\.(ts|tsx)$/, 51 | exclude: /node_modules/, 52 | use: ['ts-loader'], 53 | }, 54 | ], 55 | }, 56 | resolve: { 57 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 58 | }, 59 | performance: { 60 | hints: false, 61 | maxEntrypointSize: 512000, 62 | maxAssetSize: 512000, 63 | }, 64 | }; 65 | --------------------------------------------------------------------------------