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
///
///