From 3ddf71bed37cfb756a8f523203ad633bb0939c67 Mon Sep 17 00:00:00 2001 From: Dave Skender <8432125+DaveSkender@users.noreply.github.com> Date: Fri, 6 Sep 2024 19:57:23 -0400 Subject: [PATCH] chore: Update config, git attributes (#360) --- .editorconfig | 6 +- .gitattributes | 65 +------ .github/workflows/deploy-website.yml | 13 +- Client/package-lock.json | 200 +++++++++++----------- Client/package.json | 28 +-- Server/.editorconfig | 8 +- Server/Functions/UpdateQuotes.cs | 4 +- Server/WebApi/Services/Service.Quotes.cs | 15 +- Server/WebApi/Services/Service.Storage.cs | 68 +++----- Server/WebApi/Startup.cs | 14 +- package-lock.json | 6 - 11 files changed, 173 insertions(+), 254 deletions(-) delete mode 100644 package-lock.json diff --git a/.editorconfig b/.editorconfig index ebb9ce78..388b5d31 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,9 @@ # top-most EditorConfig file root = true +# see Client and Server folders for +# language unique editor configurations + # global baselines [*] charset = utf-8 @@ -18,6 +21,3 @@ insert_final_newline = true [*.{xml,yml}] indent_style = space indent_size = 2 - -# see Client and Server folders for -# language unique editor configurations diff --git a/.gitattributes b/.gitattributes index 1ff0c423..46291396 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,63 +1,2 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain +# Normalize line endings. +* text=lf diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index 4a472f03..5b0985d6 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -24,6 +24,17 @@ jobs: dotnet-version: "8.x" dotnet-quality: "ga" + - name: Define cache marker + id: marker + run: echo "version=$(date +'%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_OUTPUT + + - name: Replace cache markers + uses: jacobtomlinson/gha-find-replace@v3 + with: + find: "YYYY.MM.DD" + replace: "${{ steps.marker.outputs.version }}" + regex: false + - name: Build .NET solution run: > dotnet build Server/ChartBackend.sln @@ -78,7 +89,7 @@ jobs: working-directory: Client run: npm install - - name: Define marker + - name: Define cache marker id: marker run: echo "version=$(date +'%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_OUTPUT diff --git a/Client/package-lock.json b/Client/package-lock.json index e2e4df30..28401e70 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -6,17 +6,17 @@ "": { "name": "stock.charts", "dependencies": { - "@angular/animations": "18.2.2", - "@angular/cdk": "18.2.2", - "@angular/common": "18.2.2", - "@angular/compiler": "18.2.2", - "@angular/core": "18.2.2", - "@angular/forms": "18.2.2", - "@angular/material": "18.2.2", - "@angular/platform-browser": "18.2.2", - "@angular/platform-browser-dynamic": "18.2.2", - "@angular/router": "18.2.2", - "@angular/service-worker": "18.2.2", + "@angular/animations": "18.2.3", + "@angular/cdk": "18.2.3", + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/forms": "18.2.3", + "@angular/material": "18.2.3", + "@angular/platform-browser": "18.2.3", + "@angular/platform-browser-dynamic": "18.2.3", + "@angular/router": "18.2.3", + "@angular/service-worker": "18.2.3", "@ctrl/tinycolor": "4.1.0", "@ng-matero/extensions": "18.2.0", "chart.js": "4.4.4", @@ -30,9 +30,9 @@ "zone.js": "0.14.10" }, "devDependencies": { - "@angular/build": "18.2.2", - "@angular/cli": "18.2.2", - "@angular/compiler-cli": "18.2.2", + "@angular/build": "18.2.3", + "@angular/cli": "18.2.3", + "@angular/compiler-cli": "18.2.3", "typescript": "5.5.4" } }, @@ -50,13 +50,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.2.tgz", - "integrity": "sha512-LPRl9jhcf0NgshaL6RoUy1uL/cAyNt7oxctoZ9EHUu8eh5E9W/jZGhVowjOLpirwqYhmEzKJJIeS49Ssqs3RQg==", + "version": "0.1802.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.3.tgz", + "integrity": "sha512-WQ2AmkUKy1bqrDlNfozW8+VT2Tv/Fdmu4GIXps3ytZANyAKiIvTzmmql2cRCXXraa9FNMjLWNvz+qolDxWVdYQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.2", + "@angular-devkit/core": "18.2.3", "rxjs": "7.8.1" }, "engines": { @@ -66,9 +66,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.2.tgz", - "integrity": "sha512-Zz0tGptI/QQnUBDdp+1G5wGwQWMjpfe2oO+UohkrDVgFS71yVj4VDnOy51kMTxBvzw+36evTgthPpmzqPIfxBw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.3.tgz", + "integrity": "sha512-vbFs+ofNK9OWeMIcFarFjegXVklhtSdLTEFKZ9trDVr8alTJdjI9AiYa6OOUTDAyq0hqYxV26xlCisWAPe7s5w==", "dev": true, "license": "MIT", "dependencies": { @@ -94,13 +94,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.2.tgz", - "integrity": "sha512-PU6+3nX+gQ3gofR7BGwXuvNUNeeV2raURaZjlPfGpBqjyTBxukMV71QsTTWptAZT4WibCWkTFp6X1gvsOGbjMg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.3.tgz", + "integrity": "sha512-N3tRAzBW2yWQhebvc1Ha18XTMSXOQTfr8HNjx7Fasx0Fg1tNyGR612MJNZw6je/PqyItKeUHOhztvFMfCQjRyg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.2", + "@angular-devkit/core": "18.2.3", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -113,9 +113,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.2.tgz", - "integrity": "sha512-jh/dGrY77HGm54HdTiQsxmvoRfFeJgHeWAK2+nWCPoc4b7OHcWxy/04cYffs0/27ThmABmppP7ERAyZ0f60uow==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.3.tgz", + "integrity": "sha512-rIATopHr83lYR0X05buHeHssq9CGw0I0YPIQcpUTGnlqIpJcQVCf7jCFn4KGZrE9V55hFY3MD4S28njlwCToQQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -124,18 +124,18 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2" + "@angular/core": "18.2.3" } }, "node_modules/@angular/build": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.2.tgz", - "integrity": "sha512-okaDdTMXnDhvnnnih6rPQnexL6htfEAPr19bB1Ci9d31gEjVuKZCjlcw2sPZ6BUyilwC9nZlCI5vbH1Ljf6mzA==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.3.tgz", + "integrity": "sha512-USrD2Zvcb1te2dnqhH7JZ5XeJDg/t7fjUHR4f93vvMrnrncwCjLoHbHpz01HCHfcIVRgsYUdAmAi1iG7vpak7w==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.2", + "@angular-devkit/architect": "0.1802.3", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -628,9 +628,9 @@ } }, "node_modules/@angular/cdk": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.2.tgz", - "integrity": "sha512-+u7ZcMA24WO03vDzlBJJWq+okZLFDeW9JrtHzrdiT09FDt4sdUp+7PddXaZcRHIXjJL+CaCLQ6slaqPNEufqgg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.3.tgz", + "integrity": "sha512-lUcpYTxPZuntJ1FK7V2ugapCGMIhT6TUDjIGgXfS9AxGSSKgwr8HNs6Ze9pcjYC44UhP40sYAZuiaFwmE60A2A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -645,18 +645,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.2.tgz", - "integrity": "sha512-HVVaMxnbID0q+V3KE+JqzGbPHcBUFo1RKhBZ/jxY7USZNzgtyYbRc0IYqPWNdr99UT5QefTJrjVazJo1nqQZvQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz", + "integrity": "sha512-40258vuliH6+p8QSByZe5EcIXSj0iR3PNF6yuusClR/ByToHOnmuPw7WC+AYr0ooozmqlim/EjQe4/037OUB3w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.2", - "@angular-devkit/core": "18.2.2", - "@angular-devkit/schematics": "18.2.2", + "@angular-devkit/architect": "0.1802.3", + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.2", + "@schematics/angular": "18.2.3", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -679,9 +679,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.2.tgz", - "integrity": "sha512-AQe4xnnNNch/sXRnV82C8FmhijxPATKfPGojC2qbAG2o6VkWKgt5Lbj0O8WxvSIOS5Syedv+O2kLY/JMGWHNtw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz", + "integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -690,14 +690,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2", + "@angular/core": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.2.tgz", - "integrity": "sha512-gmVNCXZiv/CIk2eKRLnH19N9VsPuE2s3Oxm0MNi003zk1cLy7D4YEm4fSrjKXtPY8MMpRXiu5f63W94hLwWEVw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.3.tgz", + "integrity": "sha512-Il3ljs0j1GaYoqYFdShjUP1ryck5xTOaA8uQuRgqwU0FOwEDfugSAM3Qf7nJx/sgxTM0Lm/Nrdv2u6i1gZWeuQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -706,7 +706,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.2" + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/core": { @@ -715,9 +715,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.2.tgz", - "integrity": "sha512-fF7lDrTA12YGqVjF4LyMi4hm58cv9G6CWmzSlvun0nMYCwrbRNnakZsj19dOfiIqqu4MwHaF4w3PTmUSxkMuiw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.3.tgz", + "integrity": "sha512-BcmqYKnkcJTkGjuPztClZNQve7tdI290J5F3iZBx6c7/vaw8EU8EGZtpWYZpgiVn5S6jhcKyc1dLF9ggO9vftg==", "dev": true, "license": "MIT", "dependencies": { @@ -739,14 +739,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.2", + "@angular/compiler": "18.2.3", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.2.tgz", - "integrity": "sha512-Rx6XajL0Ydj9hXUSPDvL2Q/kMzWtbiE3VxZFJnkE+fLQiWvr0GncB+NTb/nQ6QlPQ0ly60DvuI3KLcGDuFtGVA==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz", + "integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -760,9 +760,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.2.tgz", - "integrity": "sha512-K8cv0w6o7+ocQfUrdSA3XaKrYfa1+2TlmtyxPHjEd2mCu2R+Yqo5RqJ3P8keFewJ1+bSLhz6xnn6mumwl0RnUQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.3.tgz", + "integrity": "sha512-+OBaAH0e8hue9eyLnbgpxg1/X9fps6bwXECfJ0nL5BDPU5itZ428YJbEnj5bTx0hEbqfTRiV4LgexdI+D9eOpw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -771,23 +771,23 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.2.tgz", - "integrity": "sha512-c+EQo1GEvM2w3qasgV/BGxB0bpJeSGs2WcMVTXCYVMcqEk8nwpALwfZiCAYl8JoKoiC5k993zz19xP2Eu14qkQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.3.tgz", + "integrity": "sha512-JFfvXaMHMhskncaxxus4sDvie9VYdMkfYgfinkLXpZlPFyn1IzjDw0c1BcrcsuD7UxQVZ/v5tucCgq1FQfGRpA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.2", + "@angular/cdk": "18.2.3", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -796,9 +796,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.2.tgz", - "integrity": "sha512-Bfvl8elCFxyJ9vlwamr4X5sVMcp/tSwBal2coyl0WR+/PH2PAAtf+/WMYxIN90yZmPiJx6RZWUSJRlHOFiFp3A==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz", + "integrity": "sha512-M2ob4zN7tAcL2mx7U6KnZNqNFPFl9MlPBE0FrjQjIzAjU0wSYPIJXmaPu9aMUp9niyo+He5iX98I+URi2Yc99g==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -807,9 +807,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.2", - "@angular/common": "18.2.2", - "@angular/core": "18.2.2" + "@angular/animations": "18.2.3", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/animations": { @@ -818,9 +818,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.2.tgz", - "integrity": "sha512-UM/+1nY4iIj1v4lxAmV3XRHPAh/4qfNKScCLq8tJGot64rPCbtCl0Rl8rFFGqxAFvTErVDaJycUgWNZSfVl/hw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.3.tgz", + "integrity": "sha512-nWi9ZxN4KpbJkttIckFO1PCoW0+gb/18xFO+JWyLBAtcbsudj/Mv0P/fdOaSfQdLkPhZfORr3ZcfiTkhmuGyEg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -829,16 +829,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/compiler": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2" + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3" } }, "node_modules/@angular/router": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.2.tgz", - "integrity": "sha512-tBHwuNtZNjzYAoVdveTI1ke/ZnQjKhc7gqDk9HCH2JUpdQhGbTvCKwDM51ktJpPMPcZlA263lQyy7VIyvdtK0A==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.3.tgz", + "integrity": "sha512-fvD9eSDIiIbeYoUokoWkXzu7/ZaxlzKPUHFqX1JuKuH5ciQDeT/d7lp4mj31Bxammhohzi3+z12THJYsCkj/iQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -847,16 +847,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/core": "18.2.2", - "@angular/platform-browser": "18.2.2", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.2.tgz", - "integrity": "sha512-az0v0gNkAjOQ4DThDWfNJv2DkH63B4Vj/WnXd8pbY/C7Be6w3S1mN2y9vJClWAzUH/GSLQHnOrZJfnZtTc8M0w==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.3.tgz", + "integrity": "sha512-KplaBYhhwsM3gPeOImfDGhAknN+BIcZJkHl8YRnhoUEFHsTZ8LTV02C4LWQL3YTu3pK+uj/lPMKi1CA37cXQ8g==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -868,8 +868,8 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.2", - "@angular/core": "18.2.2" + "@angular/common": "18.2.3", + "@angular/core": "18.2.3" } }, "node_modules/@babel/code-frame": { @@ -2203,9 +2203,9 @@ } }, "node_modules/@ng-select/ng-select": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-13.7.0.tgz", - "integrity": "sha512-GMNu3bLYxWAbgy9pXZ4RgnWp/cxRcrWRQdxLLyg8p9gMCLpim1p4TXR8laXJKK25MKG/LEaWgs+90yCVOoWgZA==", + "version": "13.7.1", + "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-13.7.1.tgz", + "integrity": "sha512-v/GwSBpuHd31DyoYFQECh+rCwn7xmCBpwMQTcwWerKaDQSr1egpGPSnCq2SzvfHqiJ5e1ckx7ZNTuk+swBweag==", "license": "MIT", "dependencies": { "tslib": "^2.3.1" @@ -2636,14 +2636,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.2.tgz", - "integrity": "sha512-0uPA1kQ38RnbNrzMlveX/QAqQIDu2INl5IYd3EUbJZRfYSp1VVyOSyuIBJ+1iUl5Y5VUa2uylaVZXhFdKWprXw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.3.tgz", + "integrity": "sha512-whSON70z9HYb4WboVXmPFE/RLKJJQLWNzNcUyi8OSDZkQbJnYgPp0///n738m26Y/XeJDv11q1gESy+Zl2AdUw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.2", - "@angular-devkit/schematics": "18.2.2", + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", "jsonc-parser": "3.3.1" }, "engines": { diff --git a/Client/package.json b/Client/package.json index 93a02d98..3bb61b52 100644 --- a/Client/package.json +++ b/Client/package.json @@ -9,17 +9,17 @@ }, "private": false, "dependencies": { - "@angular/animations": "18.2.2", - "@angular/cdk": "18.2.2", - "@angular/common": "18.2.2", - "@angular/compiler": "18.2.2", - "@angular/core": "18.2.2", - "@angular/forms": "18.2.2", - "@angular/material": "18.2.2", - "@angular/platform-browser": "18.2.2", - "@angular/platform-browser-dynamic": "18.2.2", - "@angular/router": "18.2.2", - "@angular/service-worker": "18.2.2", + "@angular/animations": "18.2.3", + "@angular/cdk": "18.2.3", + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/forms": "18.2.3", + "@angular/material": "18.2.3", + "@angular/platform-browser": "18.2.3", + "@angular/platform-browser-dynamic": "18.2.3", + "@angular/router": "18.2.3", + "@angular/service-worker": "18.2.3", "@ctrl/tinycolor": "4.1.0", "@ng-matero/extensions": "18.2.0", "chart.js": "4.4.4", @@ -33,9 +33,9 @@ "zone.js": "0.14.10" }, "devDependencies": { - "@angular/build": "18.2.2", - "@angular/cli": "18.2.2", - "@angular/compiler-cli": "18.2.2", + "@angular/build": "18.2.3", + "@angular/cli": "18.2.3", + "@angular/compiler-cli": "18.2.3", "typescript": "5.5.4" } } diff --git a/Server/.editorconfig b/Server/.editorconfig index ead54450..f29250cf 100644 --- a/Server/.editorconfig +++ b/Server/.editorconfig @@ -9,7 +9,6 @@ indent_size = 2 [*.{cs,vb}] tab_width = 4 indent_size = 4 -end_of_line = lf #### Naming styles #### @@ -53,11 +52,6 @@ dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - dotnet_code_quality_unused_parameters = all:suggestion dotnet_sort_system_directives_first = true @@ -71,7 +65,7 @@ dotnet_style_null_propagation = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_auto_properties = true:silent dotnet_style_object_initializer = true:suggestion -dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_prefer_collection_expression = false:suggestion [*.cs] # ref: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options diff --git a/Server/Functions/UpdateQuotes.cs b/Server/Functions/UpdateQuotes.cs index 021461a2..d281e8d0 100644 --- a/Server/Functions/UpdateQuotes.cs +++ b/Server/Functions/UpdateQuotes.cs @@ -15,7 +15,7 @@ public class Jobs(ILoggerFactory loggerFactory) /// Schedule to get and cache quotes from source feed. /// /// CRON-based schedule - /// + /// Depends on TZ environment settings for EST time zone [Function("UpdateQuotes")] public async Task Run([TimerTrigger("0 */1 08-18 * * 1-5")] TimerInfo myTimer) { @@ -84,7 +84,7 @@ private async Task StoreQuoteDaily(string symbol) string json = JsonSerializer.Serialize(quotes.OrderBy(x => x.Date)); - // store in Azure Blog + // store in Azure Blob Storage string blobName = $"{symbol}-DAILY.json"; await Storage.PutBlob(blobName, json); diff --git a/Server/WebApi/Services/Service.Quotes.cs b/Server/WebApi/Services/Service.Quotes.cs index b4bfb1ad..b357befc 100644 --- a/Server/WebApi/Services/Service.Quotes.cs +++ b/Server/WebApi/Services/Service.Quotes.cs @@ -4,16 +4,16 @@ namespace WebApi.Services; public class QuoteService { - private readonly IEnumerable backupQuotes = GetBackup(); + private static readonly IReadOnlyList backupQuotes = GetBackup(); /// - /// Get default quotes + /// Get default quotes /// /// List of default quotes public async Task> Get() => await Get("QQQ"); /// - /// Get quotes for a specific symbol. + /// Get quotes for a specific symbol. /// /// "SPY" or "QQQ" only, for now public async Task> Get(string symbol) @@ -32,7 +32,7 @@ public async Task> Get(string symbol) return quotes == null || quotes.Count == 0 ? backupQuotes - : [.. quotes.OrderBy(x => x.Date)]; + : [.. quotes]; // pre-sorted } catch { @@ -40,7 +40,9 @@ public async Task> Get(string symbol) } } - private static IEnumerable GetBackup() + #region Backup Quotes (for failover) + + private static List GetBackup() { List h = [ @@ -550,6 +552,7 @@ private static IEnumerable GetBackup() new Quote { Date = DateTime.Parse("2017-01-01"), Open = 212.61m, High = 213.35m, Low = 211.52m, Close = 211.60m, Volume = 86708880 }, ]; - return h.OrderBy(x => x.Date); + return [.. h.OrderBy(x => x.Date)]; } + #endregion } diff --git a/Server/WebApi/Services/Service.Storage.cs b/Server/WebApi/Services/Service.Storage.cs index 2cb4bfc7..5bfd67ce 100644 --- a/Server/WebApi/Services/Service.Storage.cs +++ b/Server/WebApi/Services/Service.Storage.cs @@ -2,44 +2,40 @@ namespace WebApi.Services; public static class Storage { + private static readonly string containerName = "chart-demo"; + private static readonly string azureWebJobStorage + = Environment.GetEnvironmentVariable("AzureWebJobsStorage") + ?? "UseDevelopmentStorage=true"; // failover to Azurite dev storage + /// - /// Initialize Azure services (setup blob storage for quotes) + /// Initialize Azure services (setup blob storage for quotes) /// /// - /// - public static async Task Initialize(CancellationToken cancellationToken) + public static async Task Initialize( + ILogger logger, + CancellationToken cancellationToken) { - // failover to Azurite local dev storage - string awjs = Environment - .GetEnvironmentVariable("AzureWebJobsStorage") - ?? "UseDevelopmentStorage=true"; + logger.LogInformation("API initializing ..."); // main blob container - BlobContainerClient blobContainer = new(awjs, "chart-demo"); + BlobContainerClient blobContainer = new(azureWebJobStorage, containerName); Response response = await blobContainer .CreateIfNotExistsAsync(cancellationToken: cancellationToken); - // when new container - if (response != null) - { - Console.WriteLine( - $"NOTE: New blob container `chart-demo` created {response.Value}."); - } - else - { - Console.WriteLine( - $"NOTE: Blob container already exists."); - } + string message = response != null + ? $"New `{containerName}` blob container created." + : $"Existing `{containerName}` blob container found."; + + logger.LogInformation("Blob container status: {message}", message); } /// - /// Upload/save blob item (CSV quotes) + /// Upload/save blob item (JSON quotes) /// /// Unique name of blob item - /// CSV payload to store - /// True/false success - public static async Task PutBlob(string blobName, string csv) + /// JSON payload to store + public static async Task PutBlob(string blobName, string csv) { BlobClient blob = GetBlobClient(blobName); BlobHttpHeaders httpHeader = new() { @@ -49,36 +45,16 @@ public static async Task PutBlob(string blobName, string csv) using MemoryStream ms = new(Encoding.UTF8.GetBytes(csv)); ms.Position = 0; await blob.UploadAsync(ms, httpHeader); - return true; } /// - /// Get Azure Blob client + /// Get Azure Blob client /// /// Unique name of blob item /// internal static BlobClient GetBlobClient(string blobName) { - BlobContainerClient blobContainer - = GetContainerClient("chart-demo"); - - BlobClient blob - = blobContainer.GetBlobClient(blobName); - - return blob; - } - - /// - /// Get blob storage container client - /// - /// Unique name of blob container (e.g. "chart-demo") - /// - private static BlobContainerClient GetContainerClient(string containerName) - { - // failover to Azurite local dev storage - string awjs = Environment - .GetEnvironmentVariable("AzureWebJobsStorage") - ?? "UseDevelopmentStorage=true"; - return new BlobContainerClient(awjs, containerName); + BlobContainerClient blobContainer = new(azureWebJobStorage, containerName); + return blobContainer.GetBlobClient(blobName); } } diff --git a/Server/WebApi/Startup.cs b/Server/WebApi/Startup.cs index c0854286..44dc8f50 100644 --- a/Server/WebApi/Startup.cs +++ b/Server/WebApi/Startup.cs @@ -1,18 +1,20 @@ namespace WebApi.Services; -public class StartupService : IHostedService +public class StartupService(ILoggerFactory loggerFactory) : IHostedService { + private readonly ILogger _logger = loggerFactory.CreateLogger(); + /// - /// The code in here will run when the application starts, - /// and block the startup process until finished + /// The code in here will run when the application starts, + /// and block the startup process until finished /// ///