From 54f5480cf0236ed2ee328aecbc001e4359071ff1 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 10:51:26 +0100 Subject: [PATCH 001/129] add Stats Page --- src/Web/Pages/Stats.razor | 3 +++ src/Web/Pages/Stats.razor.cs | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 src/Web/Pages/Stats.razor create mode 100644 src/Web/Pages/Stats.razor.cs diff --git a/src/Web/Pages/Stats.razor b/src/Web/Pages/Stats.razor new file mode 100644 index 00000000..a6061417 --- /dev/null +++ b/src/Web/Pages/Stats.razor @@ -0,0 +1,3 @@ +@page "/stats" + +

Stats

\ No newline at end of file diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs new file mode 100644 index 00000000..646e9870 --- /dev/null +++ b/src/Web/Pages/Stats.razor.cs @@ -0,0 +1,5 @@ +namespace Web.Pages; + +public partial class Stats +{ +} \ No newline at end of file From 4ffd9f9369c4cd254662cd9f216f3e338b8ef339 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 10:51:35 +0100 Subject: [PATCH 002/129] add Navbar --- src/Web/Pages/GamblingHelper.razor | 2 +- src/Web/Shared/Components/Navbar.razor | 9 +++++++++ src/Web/Shared/Components/Navbar.razor.css | 17 +++++++++++++++++ src/Web/Shared/Components/Theme.razor | 8 +++----- src/Web/Shared/Components/Theme.razor.css | 6 ------ 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/Web/Shared/Components/Navbar.razor create mode 100644 src/Web/Shared/Components/Navbar.razor.css diff --git a/src/Web/Pages/GamblingHelper.razor b/src/Web/Pages/GamblingHelper.razor index 8c8d609f..df2a2584 100644 --- a/src/Web/Pages/GamblingHelper.razor +++ b/src/Web/Pages/GamblingHelper.razor @@ -2,7 +2,7 @@ PoE Gambling Helper - +
icon
diff --git a/src/Web/Shared/Components/Navbar.razor b/src/Web/Shared/Components/Navbar.razor new file mode 100644 index 00000000..40245837 --- /dev/null +++ b/src/Web/Shared/Components/Navbar.razor @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/src/Web/Shared/Components/Navbar.razor.css b/src/Web/Shared/Components/Navbar.razor.css new file mode 100644 index 00000000..f11d1a11 --- /dev/null +++ b/src/Web/Shared/Components/Navbar.razor.css @@ -0,0 +1,17 @@ +nav { + display: flex; + align-items: center; + padding: 0 1rem; + margin: 0 -1.5rem; + width: 100vw; + height: 5vh; + background-color: var(--bs-body-bg-accent); +} + +nav ::deep a.test { + color: var(--bs-link-color); +} + +nav ::deep a.active { + filter: brightness(80%); +} \ No newline at end of file diff --git a/src/Web/Shared/Components/Theme.razor b/src/Web/Shared/Components/Theme.razor index 1e44f6e0..12a4f225 100644 --- a/src/Web/Shared/Components/Theme.razor +++ b/src/Web/Shared/Components/Theme.razor @@ -1,5 +1,3 @@ - \ No newline at end of file + +
+
\ No newline at end of file diff --git a/src/Web/Shared/Components/Theme.razor.css b/src/Web/Shared/Components/Theme.razor.css index 01cf13c6..83665795 100644 --- a/src/Web/Shared/Components/Theme.razor.css +++ b/src/Web/Shared/Components/Theme.razor.css @@ -1,9 +1,3 @@ -.fixed-position { - position: fixed; - top: 1rem; - left: 1rem; -} - .icon-anchor { cursor: pointer; margin: 0; From ba3d327d645c14b90857a3dc4fec98b27d06bcc8 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 11:19:00 +0100 Subject: [PATCH 003/129] fix Dispose --- PoEGamblingHelper.sln | 6 ++++ src/Web/Pages/GamblingHelper.razor.cs | 35 ++++++++++++++++++++-- src/Web/Shared/Components/Navbar.razor.css | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/PoEGamblingHelper.sln b/PoEGamblingHelper.sln index 7678fc08..4197b025 100644 --- a/PoEGamblingHelper.sln +++ b/PoEGamblingHelper.sln @@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Test", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Test", "test\Web.Test\Web.Test.csproj", "{72665095-42B7-4E4A-8019-1B6EF259C31B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp1", "BlazorApp1\BlazorApp1.csproj", "{1551E21D-51D1-43BB-91EF-6CAB9B797274}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,5 +62,9 @@ Global {72665095-42B7-4E4A-8019-1B6EF259C31B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.Build.0 = Release|Any CPU + {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 1f25babe..eeee34c7 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -9,7 +9,7 @@ namespace Web.Pages; -public partial class GamblingHelper : IDisposable +public partial class GamblingHelper : IAsyncDisposable { private readonly List _gems = new(); private List _currency = new(); @@ -28,7 +28,6 @@ public partial class GamblingHelper : IDisposable [Inject] private IJSRuntime JsRuntime { get; set; } = null!; - public void Dispose() { _loadGamblingDataTask.Dispose(); } private DateTime NextBackendUpdate() { return _lastBackendUpdate.AddMinutes(5); } protected override async Task OnInitializedAsync() @@ -36,16 +35,19 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); var filterValues = await LocalStorage.GetItemAsync("GemDataQuery"); if (filterValues is not null) _filterValues = filterValues; + _loadGamblingDataTask = Task.Run(async () => { while (true) { while (_isUpdating || NextBackendUpdate() > DateTime.Now) { + _tokenSource.Token.ThrowIfCancellationRequested(); await InvokeAsync(StateHasChanged); await Task.Delay(1000); } + _tokenSource.Token.ThrowIfCancellationRequested(); await LoadGamblingData(); await InvokeAsync(StateHasChanged); } @@ -130,6 +132,35 @@ private async Task UpdateGems() return true; } + #region Dispose + + private readonly CancellationTokenSource _tokenSource = new(); + + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore().ConfigureAwait(false); + GC.SuppressFinalize(this); + } + + protected virtual async ValueTask DisposeAsyncCore() + { + _tokenSource.Cancel(); + try + { + await _loadGamblingDataTask; + } + catch (OperationCanceledException) + { + } + finally + { + _loadGamblingDataTask.Dispose(); + _tokenSource.Dispose(); + } + } + + #endregion + #region OnScrollToBottom private readonly List _positionsY = new(); diff --git a/src/Web/Shared/Components/Navbar.razor.css b/src/Web/Shared/Components/Navbar.razor.css index f11d1a11..3c7968e7 100644 --- a/src/Web/Shared/Components/Navbar.razor.css +++ b/src/Web/Shared/Components/Navbar.razor.css @@ -13,5 +13,5 @@ nav ::deep a.test { } nav ::deep a.active { - filter: brightness(80%); + color: var(--bs-secondary); } \ No newline at end of file From 9f047cd19f2036b71b8d398fa9f7df0da138e56e Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 11:36:56 +0100 Subject: [PATCH 004/129] add Planning --- src/Web/Pages/GamblingHelper.razor.cs | 1 - src/Web/Pages/Stats.razor | 15 ++++++++++++++- src/Web/Pages/Stats.razor.cs | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index eeee34c7..39af9ad4 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -25,7 +25,6 @@ public partial class GamblingHelper : IAsyncDisposable [Inject] private ICurrencyService CurrencyService { get; set; } = default!; [Inject] private ILocalStorageService LocalStorage { get; set; } = default!; [Inject] private ILeagueService LeagueService { get; set; } = default!; - [Inject] private IJSRuntime JsRuntime { get; set; } = null!; private DateTime NextBackendUpdate() { return _lastBackendUpdate.AddMinutes(5); } diff --git a/src/Web/Pages/Stats.razor b/src/Web/Pages/Stats.razor index a6061417..795ba8dc 100644 --- a/src/Web/Pages/Stats.razor +++ b/src/Web/Pages/Stats.razor @@ -1,3 +1,16 @@ @page "/stats" -

Stats

\ No newline at end of file +

Stats

+ +
+ + + + + +
+ +You have [Terrible|Bad|Mediocre|Average|Decent|Good|Amazing|Streamer RNG] Luck. +Your Luck-Rank: 654160 +You Made: 420 [Chaos|Divines] +Outcomes: PIE CHART \ No newline at end of file diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs index 646e9870..655143b1 100644 --- a/src/Web/Pages/Stats.razor.cs +++ b/src/Web/Pages/Stats.razor.cs @@ -2,4 +2,5 @@ public partial class Stats { + private bool _isMyAccountSelected = true; } \ No newline at end of file From 677d8117f2ff4a70f1deed8511f9b1bd9989cafa Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 11:45:14 +0100 Subject: [PATCH 005/129] add JavaScript MicroProject --- src/Web/JavaScript/.gitignore | 12 + .../JavaScript/.yarn/releases/yarn-3.4.1.cjs | 873 +++++++++++ src/Web/JavaScript/.yarnrc.yml | 3 + src/Web/JavaScript/package.json | 21 + src/Web/JavaScript/src/index.ts | 26 + src/Web/JavaScript/tsconfig.json | 13 + src/Web/JavaScript/webpack.config.js | 21 + src/Web/JavaScript/yarn.lock | 1335 +++++++++++++++++ src/Web/Web.csproj | 5 + 9 files changed, 2309 insertions(+) create mode 100644 src/Web/JavaScript/.gitignore create mode 100644 src/Web/JavaScript/.yarn/releases/yarn-3.4.1.cjs create mode 100644 src/Web/JavaScript/.yarnrc.yml create mode 100644 src/Web/JavaScript/package.json create mode 100644 src/Web/JavaScript/src/index.ts create mode 100644 src/Web/JavaScript/tsconfig.json create mode 100644 src/Web/JavaScript/webpack.config.js create mode 100644 src/Web/JavaScript/yarn.lock diff --git a/src/Web/JavaScript/.gitignore b/src/Web/JavaScript/.gitignore new file mode 100644 index 00000000..338b6eb6 --- /dev/null +++ b/src/Web/JavaScript/.gitignore @@ -0,0 +1,12 @@ +# yarn specific +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# general +node_modules +built \ No newline at end of file diff --git a/src/Web/JavaScript/.yarn/releases/yarn-3.4.1.cjs b/src/Web/JavaScript/.yarn/releases/yarn-3.4.1.cjs new file mode 100644 index 00000000..2bdb752d --- /dev/null +++ b/src/Web/JavaScript/.yarn/releases/yarn-3.4.1.cjs @@ -0,0 +1,873 @@ +#!/usr/bin/env node +/* eslint-disable */ +//prettier-ignore +(()=>{var Mue=Object.create;var Wb=Object.defineProperty;var Kue=Object.getOwnPropertyDescriptor;var Uue=Object.getOwnPropertyNames;var Hue=Object.getPrototypeOf,Gue=Object.prototype.hasOwnProperty;var J=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+r+'" is not supported')});var Yue=(r,e)=>()=>(r&&(e=r(r=0)),e);var w=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports),ut=(r,e)=>{for(var t in e)Wb(r,t,{get:e[t],enumerable:!0})},jue=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Uue(e))!Gue.call(r,n)&&n!==t&&Wb(r,n,{get:()=>e[n],enumerable:!(i=Kue(e,n))||i.enumerable});return r};var Pe=(r,e,t)=>(t=r!=null?Mue(Hue(r)):{},jue(e||!r||!r.__esModule?Wb(t,"default",{value:r,enumerable:!0}):t,r));var _1=w((O7e,X1)=>{X1.exports=V1;V1.sync=uge;var W1=J("fs");function cge(r,e){var t=e.pathExt!==void 0?e.pathExt:process.env.PATHEXT;if(!t||(t=t.split(";"),t.indexOf("")!==-1))return!0;for(var i=0;i{tK.exports=$1;$1.sync=gge;var Z1=J("fs");function $1(r,e,t){Z1.stat(r,function(i,n){t(i,i?!1:eK(n,e))})}function gge(r,e){return eK(Z1.statSync(r),e)}function eK(r,e){return r.isFile()&&fge(r,e)}function fge(r,e){var t=r.mode,i=r.uid,n=r.gid,s=e.uid!==void 0?e.uid:process.getuid&&process.getuid(),o=e.gid!==void 0?e.gid:process.getgid&&process.getgid(),a=parseInt("100",8),l=parseInt("010",8),c=parseInt("001",8),u=a|l,g=t&c||t&l&&n===o||t&a&&i===s||t&u&&s===0;return g}});var nK=w((U7e,iK)=>{var K7e=J("fs"),_E;process.platform==="win32"||global.TESTING_WINDOWS?_E=_1():_E=rK();iK.exports=uS;uS.sync=hge;function uS(r,e,t){if(typeof e=="function"&&(t=e,e={}),!t){if(typeof Promise!="function")throw new TypeError("callback not provided");return new Promise(function(i,n){uS(r,e||{},function(s,o){s?n(s):i(o)})})}_E(r,e||{},function(i,n){i&&(i.code==="EACCES"||e&&e.ignoreErrors)&&(i=null,n=!1),t(i,n)})}function hge(r,e){try{return _E.sync(r,e||{})}catch(t){if(e&&e.ignoreErrors||t.code==="EACCES")return!1;throw t}}});var uK=w((H7e,cK)=>{var Ig=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",sK=J("path"),pge=Ig?";":":",oK=nK(),aK=r=>Object.assign(new Error(`not found: ${r}`),{code:"ENOENT"}),AK=(r,e)=>{let t=e.colon||pge,i=r.match(/\//)||Ig&&r.match(/\\/)?[""]:[...Ig?[process.cwd()]:[],...(e.path||process.env.PATH||"").split(t)],n=Ig?e.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",s=Ig?n.split(t):[""];return Ig&&r.indexOf(".")!==-1&&s[0]!==""&&s.unshift(""),{pathEnv:i,pathExt:s,pathExtExe:n}},lK=(r,e,t)=>{typeof e=="function"&&(t=e,e={}),e||(e={});let{pathEnv:i,pathExt:n,pathExtExe:s}=AK(r,e),o=[],a=c=>new Promise((u,g)=>{if(c===i.length)return e.all&&o.length?u(o):g(aK(r));let f=i[c],h=/^".*"$/.test(f)?f.slice(1,-1):f,p=sK.join(h,r),C=!h&&/^\.[\\\/]/.test(r)?r.slice(0,2)+p:p;u(l(C,c,0))}),l=(c,u,g)=>new Promise((f,h)=>{if(g===n.length)return f(a(u+1));let p=n[g];oK(c+p,{pathExt:s},(C,y)=>{if(!C&&y)if(e.all)o.push(c+p);else return f(c+p);return f(l(c,u,g+1))})});return t?a(0).then(c=>t(null,c),t):a(0)},dge=(r,e)=>{e=e||{};let{pathEnv:t,pathExt:i,pathExtExe:n}=AK(r,e),s=[];for(let o=0;o{"use strict";var gK=(r={})=>{let e=r.env||process.env;return(r.platform||process.platform)!=="win32"?"PATH":Object.keys(e).reverse().find(i=>i.toUpperCase()==="PATH")||"Path"};gS.exports=gK;gS.exports.default=gK});var CK=w((Y7e,dK)=>{"use strict";var hK=J("path"),Cge=uK(),mge=fK();function pK(r,e){let t=r.options.env||process.env,i=process.cwd(),n=r.options.cwd!=null,s=n&&process.chdir!==void 0&&!process.chdir.disabled;if(s)try{process.chdir(r.options.cwd)}catch{}let o;try{o=Cge.sync(r.command,{path:t[mge({env:t})],pathExt:e?hK.delimiter:void 0})}catch{}finally{s&&process.chdir(i)}return o&&(o=hK.resolve(n?r.options.cwd:"",o)),o}function Ege(r){return pK(r)||pK(r,!0)}dK.exports=Ege});var mK=w((j7e,hS)=>{"use strict";var fS=/([()\][%!^"`<>&|;, *?])/g;function Ige(r){return r=r.replace(fS,"^$1"),r}function yge(r,e){return r=`${r}`,r=r.replace(/(\\*)"/g,'$1$1\\"'),r=r.replace(/(\\*)$/,"$1$1"),r=`"${r}"`,r=r.replace(fS,"^$1"),e&&(r=r.replace(fS,"^$1")),r}hS.exports.command=Ige;hS.exports.argument=yge});var IK=w((q7e,EK)=>{"use strict";EK.exports=/^#!(.*)/});var wK=w((J7e,yK)=>{"use strict";var wge=IK();yK.exports=(r="")=>{let e=r.match(wge);if(!e)return null;let[t,i]=e[0].replace(/#! ?/,"").split(" "),n=t.split("/").pop();return n==="env"?i:i?`${n} ${i}`:n}});var QK=w((W7e,BK)=>{"use strict";var pS=J("fs"),Bge=wK();function Qge(r){let t=Buffer.alloc(150),i;try{i=pS.openSync(r,"r"),pS.readSync(i,t,0,150,0),pS.closeSync(i)}catch{}return Bge(t.toString())}BK.exports=Qge});var xK=w((z7e,vK)=>{"use strict";var bge=J("path"),bK=CK(),SK=mK(),Sge=QK(),vge=process.platform==="win32",xge=/\.(?:com|exe)$/i,Pge=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function Dge(r){r.file=bK(r);let e=r.file&&Sge(r.file);return e?(r.args.unshift(r.file),r.command=e,bK(r)):r.file}function kge(r){if(!vge)return r;let e=Dge(r),t=!xge.test(e);if(r.options.forceShell||t){let i=Pge.test(e);r.command=bge.normalize(r.command),r.command=SK.command(r.command),r.args=r.args.map(s=>SK.argument(s,i));let n=[r.command].concat(r.args).join(" ");r.args=["/d","/s","/c",`"${n}"`],r.command=process.env.comspec||"cmd.exe",r.options.windowsVerbatimArguments=!0}return r}function Rge(r,e,t){e&&!Array.isArray(e)&&(t=e,e=null),e=e?e.slice(0):[],t=Object.assign({},t);let i={command:r,args:e,options:t,file:void 0,original:{command:r,args:e}};return t.shell?i:kge(i)}vK.exports=Rge});var kK=w((V7e,DK)=>{"use strict";var dS=process.platform==="win32";function CS(r,e){return Object.assign(new Error(`${e} ${r.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${e} ${r.command}`,path:r.command,spawnargs:r.args})}function Fge(r,e){if(!dS)return;let t=r.emit;r.emit=function(i,n){if(i==="exit"){let s=PK(n,e,"spawn");if(s)return t.call(r,"error",s)}return t.apply(r,arguments)}}function PK(r,e){return dS&&r===1&&!e.file?CS(e.original,"spawn"):null}function Nge(r,e){return dS&&r===1&&!e.file?CS(e.original,"spawnSync"):null}DK.exports={hookChildProcess:Fge,verifyENOENT:PK,verifyENOENTSync:Nge,notFoundError:CS}});var IS=w((X7e,yg)=>{"use strict";var RK=J("child_process"),mS=xK(),ES=kK();function FK(r,e,t){let i=mS(r,e,t),n=RK.spawn(i.command,i.args,i.options);return ES.hookChildProcess(n,i),n}function Lge(r,e,t){let i=mS(r,e,t),n=RK.spawnSync(i.command,i.args,i.options);return n.error=n.error||ES.verifyENOENTSync(n.status,i),n}yg.exports=FK;yg.exports.spawn=FK;yg.exports.sync=Lge;yg.exports._parse=mS;yg.exports._enoent=ES});var LK=w((_7e,NK)=>{"use strict";function Tge(r,e){function t(){this.constructor=r}t.prototype=e.prototype,r.prototype=new t}function Ml(r,e,t,i){this.message=r,this.expected=e,this.found=t,this.location=i,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,Ml)}Tge(Ml,Error);Ml.buildMessage=function(r,e){var t={literal:function(c){return'"'+n(c.text)+'"'},class:function(c){var u="",g;for(g=0;g0){for(g=1,f=1;g>",ie=me(">>",!1),de=">&",tt=me(">&",!1),Pt=">",It=me(">",!1),Or="<<<",ii=me("<<<",!1),gi="<&",hr=me("<&",!1),fi="<",ni=me("<",!1),Ls=function(m){return{type:"argument",segments:[].concat(...m)}},pr=function(m){return m},Ei="$'",_n=me("$'",!1),oa="'",aA=me("'",!1),eg=function(m){return[{type:"text",text:m}]},Zn='""',AA=me('""',!1),aa=function(){return{type:"text",text:""}},up='"',lA=me('"',!1),cA=function(m){return m},wr=function(m){return{type:"arithmetic",arithmetic:m,quoted:!0}},wl=function(m){return{type:"shell",shell:m,quoted:!0}},tg=function(m){return{type:"variable",...m,quoted:!0}},po=function(m){return{type:"text",text:m}},rg=function(m){return{type:"arithmetic",arithmetic:m,quoted:!1}},gp=function(m){return{type:"shell",shell:m,quoted:!1}},fp=function(m){return{type:"variable",...m,quoted:!1}},vr=function(m){return{type:"glob",pattern:m}},se=/^[^']/,Co=Je(["'"],!0,!1),Dn=function(m){return m.join("")},ig=/^[^$"]/,Qt=Je(["$",'"'],!0,!1),Bl=`\\ +`,kn=me(`\\ +`,!1),$n=function(){return""},es="\\",gt=me("\\",!1),mo=/^[\\$"`]/,At=Je(["\\","$",'"',"`"],!1,!1),an=function(m){return m},S="\\a",Tt=me("\\a",!1),ng=function(){return"a"},Ql="\\b",hp=me("\\b",!1),pp=function(){return"\b"},dp=/^[Ee]/,Cp=Je(["E","e"],!1,!1),mp=function(){return"\x1B"},G="\\f",yt=me("\\f",!1),uA=function(){return"\f"},ji="\\n",bl=me("\\n",!1),Xe=function(){return` +`},Aa="\\r",sg=me("\\r",!1),bE=function(){return"\r"},Ep="\\t",SE=me("\\t",!1),ar=function(){return" "},Rn="\\v",Sl=me("\\v",!1),Ip=function(){return"\v"},Ts=/^[\\'"?]/,la=Je(["\\","'",'"',"?"],!1,!1),An=function(m){return String.fromCharCode(parseInt(m,16))},Te="\\x",og=me("\\x",!1),vl="\\u",Os=me("\\u",!1),xl="\\U",gA=me("\\U",!1),ag=function(m){return String.fromCodePoint(parseInt(m,16))},Ag=/^[0-7]/,ca=Je([["0","7"]],!1,!1),ua=/^[0-9a-fA-f]/,rt=Je([["0","9"],["a","f"],["A","f"]],!1,!1),Eo=nt(),fA="-",Pl=me("-",!1),Ms="+",Dl=me("+",!1),vE=".",yp=me(".",!1),lg=function(m,b,N){return{type:"number",value:(m==="-"?-1:1)*parseFloat(b.join("")+"."+N.join(""))}},wp=function(m,b){return{type:"number",value:(m==="-"?-1:1)*parseInt(b.join(""))}},xE=function(m){return{type:"variable",...m}},kl=function(m){return{type:"variable",name:m}},PE=function(m){return m},cg="*",hA=me("*",!1),Rr="/",DE=me("/",!1),Ks=function(m,b,N){return{type:b==="*"?"multiplication":"division",right:N}},Us=function(m,b){return b.reduce((N,U)=>({left:N,...U}),m)},ug=function(m,b,N){return{type:b==="+"?"addition":"subtraction",right:N}},pA="$((",R=me("$((",!1),q="))",Ce=me("))",!1),Ke=function(m){return m},Re="$(",ze=me("$(",!1),dt=function(m){return m},Ft="${",Fn=me("${",!1),Db=":-",$M=me(":-",!1),e1=function(m,b){return{name:m,defaultValue:b}},kb=":-}",t1=me(":-}",!1),r1=function(m){return{name:m,defaultValue:[]}},Rb=":+",i1=me(":+",!1),n1=function(m,b){return{name:m,alternativeValue:b}},Fb=":+}",s1=me(":+}",!1),o1=function(m){return{name:m,alternativeValue:[]}},Nb=function(m){return{name:m}},a1="$",A1=me("$",!1),l1=function(m){return e.isGlobPattern(m)},c1=function(m){return m},Lb=/^[a-zA-Z0-9_]/,Tb=Je([["a","z"],["A","Z"],["0","9"],"_"],!1,!1),Ob=function(){return T()},Mb=/^[$@*?#a-zA-Z0-9_\-]/,Kb=Je(["$","@","*","?","#",["a","z"],["A","Z"],["0","9"],"_","-"],!1,!1),u1=/^[(){}<>$|&; \t"']/,gg=Je(["(",")","{","}","<",">","$","|","&",";"," "," ",'"',"'"],!1,!1),Ub=/^[<>&; \t"']/,Hb=Je(["<",">","&",";"," "," ",'"',"'"],!1,!1),kE=/^[ \t]/,RE=Je([" "," "],!1,!1),Q=0,Me=0,dA=[{line:1,column:1}],d=0,E=[],I=0,k;if("startRule"in e){if(!(e.startRule in i))throw new Error(`Can't start parsing from rule "`+e.startRule+'".');n=i[e.startRule]}function T(){return r.substring(Me,Q)}function _(){return Et(Me,Q)}function te(m,b){throw b=b!==void 0?b:Et(Me,Q),ki([lt(m)],r.substring(Me,Q),b)}function Be(m,b){throw b=b!==void 0?b:Et(Me,Q),Nn(m,b)}function me(m,b){return{type:"literal",text:m,ignoreCase:b}}function Je(m,b,N){return{type:"class",parts:m,inverted:b,ignoreCase:N}}function nt(){return{type:"any"}}function wt(){return{type:"end"}}function lt(m){return{type:"other",description:m}}function it(m){var b=dA[m],N;if(b)return b;for(N=m-1;!dA[N];)N--;for(b=dA[N],b={line:b.line,column:b.column};Nd&&(d=Q,E=[]),E.push(m))}function Nn(m,b){return new Ml(m,null,null,b)}function ki(m,b,N){return new Ml(Ml.buildMessage(m,b),m,b,N)}function CA(){var m,b;return m=Q,b=Mr(),b===t&&(b=null),b!==t&&(Me=m,b=s(b)),m=b,m}function Mr(){var m,b,N,U,ce;if(m=Q,b=Kr(),b!==t){for(N=[],U=He();U!==t;)N.push(U),U=He();N!==t?(U=ga(),U!==t?(ce=ts(),ce===t&&(ce=null),ce!==t?(Me=m,b=o(b,U,ce),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)}else Q=m,m=t;if(m===t)if(m=Q,b=Kr(),b!==t){for(N=[],U=He();U!==t;)N.push(U),U=He();N!==t?(U=ga(),U===t&&(U=null),U!==t?(Me=m,b=a(b,U),m=b):(Q=m,m=t)):(Q=m,m=t)}else Q=m,m=t;return m}function ts(){var m,b,N,U,ce;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t)if(N=Mr(),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();U!==t?(Me=m,b=l(N),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t;return m}function ga(){var m;return r.charCodeAt(Q)===59?(m=c,Q++):(m=t,I===0&&Qe(u)),m===t&&(r.charCodeAt(Q)===38?(m=g,Q++):(m=t,I===0&&Qe(f))),m}function Kr(){var m,b,N;return m=Q,b=g1(),b!==t?(N=yue(),N===t&&(N=null),N!==t?(Me=m,b=h(b,N),m=b):(Q=m,m=t)):(Q=m,m=t),m}function yue(){var m,b,N,U,ce,Se,ht;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t)if(N=wue(),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();if(U!==t)if(ce=Kr(),ce!==t){for(Se=[],ht=He();ht!==t;)Se.push(ht),ht=He();Se!==t?(Me=m,b=p(N,ce),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t;return m}function wue(){var m;return r.substr(Q,2)===C?(m=C,Q+=2):(m=t,I===0&&Qe(y)),m===t&&(r.substr(Q,2)===B?(m=B,Q+=2):(m=t,I===0&&Qe(v))),m}function g1(){var m,b,N;return m=Q,b=bue(),b!==t?(N=Bue(),N===t&&(N=null),N!==t?(Me=m,b=D(b,N),m=b):(Q=m,m=t)):(Q=m,m=t),m}function Bue(){var m,b,N,U,ce,Se,ht;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t)if(N=Que(),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();if(U!==t)if(ce=g1(),ce!==t){for(Se=[],ht=He();ht!==t;)Se.push(ht),ht=He();Se!==t?(Me=m,b=L(N,ce),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t;return m}function Que(){var m;return r.substr(Q,2)===H?(m=H,Q+=2):(m=t,I===0&&Qe(j)),m===t&&(r.charCodeAt(Q)===124?(m=$,Q++):(m=t,I===0&&Qe(V))),m}function FE(){var m,b,N,U,ce,Se;if(m=Q,b=Q1(),b!==t)if(r.charCodeAt(Q)===61?(N=W,Q++):(N=t,I===0&&Qe(Z)),N!==t)if(U=p1(),U!==t){for(ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();ce!==t?(Me=m,b=A(b,U),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t;else Q=m,m=t;if(m===t)if(m=Q,b=Q1(),b!==t)if(r.charCodeAt(Q)===61?(N=W,Q++):(N=t,I===0&&Qe(Z)),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();U!==t?(Me=m,b=ae(b),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t;return m}function bue(){var m,b,N,U,ce,Se,ht,Bt,Jr,hi,rs;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t)if(r.charCodeAt(Q)===40?(N=ge,Q++):(N=t,I===0&&Qe(re)),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();if(U!==t)if(ce=Mr(),ce!==t){for(Se=[],ht=He();ht!==t;)Se.push(ht),ht=He();if(Se!==t)if(r.charCodeAt(Q)===41?(ht=O,Q++):(ht=t,I===0&&Qe(F)),ht!==t){for(Bt=[],Jr=He();Jr!==t;)Bt.push(Jr),Jr=He();if(Bt!==t){for(Jr=[],hi=Bp();hi!==t;)Jr.push(hi),hi=Bp();if(Jr!==t){for(hi=[],rs=He();rs!==t;)hi.push(rs),rs=He();hi!==t?(Me=m,b=ue(ce,Jr),m=b):(Q=m,m=t)}else Q=m,m=t}else Q=m,m=t}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t;if(m===t){for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t)if(r.charCodeAt(Q)===123?(N=he,Q++):(N=t,I===0&&Qe(ke)),N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();if(U!==t)if(ce=Mr(),ce!==t){for(Se=[],ht=He();ht!==t;)Se.push(ht),ht=He();if(Se!==t)if(r.charCodeAt(Q)===125?(ht=Fe,Q++):(ht=t,I===0&&Qe(Ne)),ht!==t){for(Bt=[],Jr=He();Jr!==t;)Bt.push(Jr),Jr=He();if(Bt!==t){for(Jr=[],hi=Bp();hi!==t;)Jr.push(hi),hi=Bp();if(Jr!==t){for(hi=[],rs=He();rs!==t;)hi.push(rs),rs=He();hi!==t?(Me=m,b=oe(ce,Jr),m=b):(Q=m,m=t)}else Q=m,m=t}else Q=m,m=t}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;else Q=m,m=t;if(m===t){for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t){for(N=[],U=FE();U!==t;)N.push(U),U=FE();if(N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();if(U!==t){if(ce=[],Se=h1(),Se!==t)for(;Se!==t;)ce.push(Se),Se=h1();else ce=t;if(ce!==t){for(Se=[],ht=He();ht!==t;)Se.push(ht),ht=He();Se!==t?(Me=m,b=le(N,ce),m=b):(Q=m,m=t)}else Q=m,m=t}else Q=m,m=t}else Q=m,m=t}else Q=m,m=t;if(m===t){for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t){if(N=[],U=FE(),U!==t)for(;U!==t;)N.push(U),U=FE();else N=t;if(N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();U!==t?(Me=m,b=we(N),m=b):(Q=m,m=t)}else Q=m,m=t}else Q=m,m=t}}}return m}function f1(){var m,b,N,U,ce;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t){if(N=[],U=NE(),U!==t)for(;U!==t;)N.push(U),U=NE();else N=t;if(N!==t){for(U=[],ce=He();ce!==t;)U.push(ce),ce=He();U!==t?(Me=m,b=fe(N),m=b):(Q=m,m=t)}else Q=m,m=t}else Q=m,m=t;return m}function h1(){var m,b,N;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();if(b!==t?(N=Bp(),N!==t?(Me=m,b=Ae(N),m=b):(Q=m,m=t)):(Q=m,m=t),m===t){for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();b!==t?(N=NE(),N!==t?(Me=m,b=Ae(N),m=b):(Q=m,m=t)):(Q=m,m=t)}return m}function Bp(){var m,b,N,U,ce;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();return b!==t?(qe.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(ne)),N===t&&(N=null),N!==t?(U=Sue(),U!==t?(ce=NE(),ce!==t?(Me=m,b=Y(N,U,ce),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m}function Sue(){var m;return r.substr(Q,2)===pe?(m=pe,Q+=2):(m=t,I===0&&Qe(ie)),m===t&&(r.substr(Q,2)===de?(m=de,Q+=2):(m=t,I===0&&Qe(tt)),m===t&&(r.charCodeAt(Q)===62?(m=Pt,Q++):(m=t,I===0&&Qe(It)),m===t&&(r.substr(Q,3)===Or?(m=Or,Q+=3):(m=t,I===0&&Qe(ii)),m===t&&(r.substr(Q,2)===gi?(m=gi,Q+=2):(m=t,I===0&&Qe(hr)),m===t&&(r.charCodeAt(Q)===60?(m=fi,Q++):(m=t,I===0&&Qe(ni))))))),m}function NE(){var m,b,N;for(m=Q,b=[],N=He();N!==t;)b.push(N),N=He();return b!==t?(N=p1(),N!==t?(Me=m,b=Ae(N),m=b):(Q=m,m=t)):(Q=m,m=t),m}function p1(){var m,b,N;if(m=Q,b=[],N=d1(),N!==t)for(;N!==t;)b.push(N),N=d1();else b=t;return b!==t&&(Me=m,b=Ls(b)),m=b,m}function d1(){var m,b;return m=Q,b=vue(),b!==t&&(Me=m,b=pr(b)),m=b,m===t&&(m=Q,b=xue(),b!==t&&(Me=m,b=pr(b)),m=b,m===t&&(m=Q,b=Pue(),b!==t&&(Me=m,b=pr(b)),m=b,m===t&&(m=Q,b=Due(),b!==t&&(Me=m,b=pr(b)),m=b))),m}function vue(){var m,b,N,U;return m=Q,r.substr(Q,2)===Ei?(b=Ei,Q+=2):(b=t,I===0&&Qe(_n)),b!==t?(N=Fue(),N!==t?(r.charCodeAt(Q)===39?(U=oa,Q++):(U=t,I===0&&Qe(aA)),U!==t?(Me=m,b=eg(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m}function xue(){var m,b,N,U;return m=Q,r.charCodeAt(Q)===39?(b=oa,Q++):(b=t,I===0&&Qe(aA)),b!==t?(N=kue(),N!==t?(r.charCodeAt(Q)===39?(U=oa,Q++):(U=t,I===0&&Qe(aA)),U!==t?(Me=m,b=eg(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m}function Pue(){var m,b,N,U;if(m=Q,r.substr(Q,2)===Zn?(b=Zn,Q+=2):(b=t,I===0&&Qe(AA)),b!==t&&(Me=m,b=aa()),m=b,m===t)if(m=Q,r.charCodeAt(Q)===34?(b=up,Q++):(b=t,I===0&&Qe(lA)),b!==t){for(N=[],U=C1();U!==t;)N.push(U),U=C1();N!==t?(r.charCodeAt(Q)===34?(U=up,Q++):(U=t,I===0&&Qe(lA)),U!==t?(Me=m,b=cA(N),m=b):(Q=m,m=t)):(Q=m,m=t)}else Q=m,m=t;return m}function Due(){var m,b,N;if(m=Q,b=[],N=m1(),N!==t)for(;N!==t;)b.push(N),N=m1();else b=t;return b!==t&&(Me=m,b=cA(b)),m=b,m}function C1(){var m,b;return m=Q,b=w1(),b!==t&&(Me=m,b=wr(b)),m=b,m===t&&(m=Q,b=B1(),b!==t&&(Me=m,b=wl(b)),m=b,m===t&&(m=Q,b=qb(),b!==t&&(Me=m,b=tg(b)),m=b,m===t&&(m=Q,b=Rue(),b!==t&&(Me=m,b=po(b)),m=b))),m}function m1(){var m,b;return m=Q,b=w1(),b!==t&&(Me=m,b=rg(b)),m=b,m===t&&(m=Q,b=B1(),b!==t&&(Me=m,b=gp(b)),m=b,m===t&&(m=Q,b=qb(),b!==t&&(Me=m,b=fp(b)),m=b,m===t&&(m=Q,b=Tue(),b!==t&&(Me=m,b=vr(b)),m=b,m===t&&(m=Q,b=Lue(),b!==t&&(Me=m,b=po(b)),m=b)))),m}function kue(){var m,b,N;for(m=Q,b=[],se.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Co));N!==t;)b.push(N),se.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Co));return b!==t&&(Me=m,b=Dn(b)),m=b,m}function Rue(){var m,b,N;if(m=Q,b=[],N=E1(),N===t&&(ig.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Qt))),N!==t)for(;N!==t;)b.push(N),N=E1(),N===t&&(ig.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Qt)));else b=t;return b!==t&&(Me=m,b=Dn(b)),m=b,m}function E1(){var m,b,N;return m=Q,r.substr(Q,2)===Bl?(b=Bl,Q+=2):(b=t,I===0&&Qe(kn)),b!==t&&(Me=m,b=$n()),m=b,m===t&&(m=Q,r.charCodeAt(Q)===92?(b=es,Q++):(b=t,I===0&&Qe(gt)),b!==t?(mo.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(At)),N!==t?(Me=m,b=an(N),m=b):(Q=m,m=t)):(Q=m,m=t)),m}function Fue(){var m,b,N;for(m=Q,b=[],N=I1(),N===t&&(se.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Co)));N!==t;)b.push(N),N=I1(),N===t&&(se.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Co)));return b!==t&&(Me=m,b=Dn(b)),m=b,m}function I1(){var m,b,N;return m=Q,r.substr(Q,2)===S?(b=S,Q+=2):(b=t,I===0&&Qe(Tt)),b!==t&&(Me=m,b=ng()),m=b,m===t&&(m=Q,r.substr(Q,2)===Ql?(b=Ql,Q+=2):(b=t,I===0&&Qe(hp)),b!==t&&(Me=m,b=pp()),m=b,m===t&&(m=Q,r.charCodeAt(Q)===92?(b=es,Q++):(b=t,I===0&&Qe(gt)),b!==t?(dp.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Cp)),N!==t?(Me=m,b=mp(),m=b):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===G?(b=G,Q+=2):(b=t,I===0&&Qe(yt)),b!==t&&(Me=m,b=uA()),m=b,m===t&&(m=Q,r.substr(Q,2)===ji?(b=ji,Q+=2):(b=t,I===0&&Qe(bl)),b!==t&&(Me=m,b=Xe()),m=b,m===t&&(m=Q,r.substr(Q,2)===Aa?(b=Aa,Q+=2):(b=t,I===0&&Qe(sg)),b!==t&&(Me=m,b=bE()),m=b,m===t&&(m=Q,r.substr(Q,2)===Ep?(b=Ep,Q+=2):(b=t,I===0&&Qe(SE)),b!==t&&(Me=m,b=ar()),m=b,m===t&&(m=Q,r.substr(Q,2)===Rn?(b=Rn,Q+=2):(b=t,I===0&&Qe(Sl)),b!==t&&(Me=m,b=Ip()),m=b,m===t&&(m=Q,r.charCodeAt(Q)===92?(b=es,Q++):(b=t,I===0&&Qe(gt)),b!==t?(Ts.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(la)),N!==t?(Me=m,b=an(N),m=b):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Nue()))))))))),m}function Nue(){var m,b,N,U,ce,Se,ht,Bt,Jr,hi,rs,Jb;return m=Q,r.charCodeAt(Q)===92?(b=es,Q++):(b=t,I===0&&Qe(gt)),b!==t?(N=Gb(),N!==t?(Me=m,b=An(N),m=b):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===Te?(b=Te,Q+=2):(b=t,I===0&&Qe(og)),b!==t?(N=Q,U=Q,ce=Gb(),ce!==t?(Se=Ln(),Se!==t?(ce=[ce,Se],U=ce):(Q=U,U=t)):(Q=U,U=t),U===t&&(U=Gb()),U!==t?N=r.substring(N,Q):N=U,N!==t?(Me=m,b=An(N),m=b):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===vl?(b=vl,Q+=2):(b=t,I===0&&Qe(Os)),b!==t?(N=Q,U=Q,ce=Ln(),ce!==t?(Se=Ln(),Se!==t?(ht=Ln(),ht!==t?(Bt=Ln(),Bt!==t?(ce=[ce,Se,ht,Bt],U=ce):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t),U!==t?N=r.substring(N,Q):N=U,N!==t?(Me=m,b=An(N),m=b):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===xl?(b=xl,Q+=2):(b=t,I===0&&Qe(gA)),b!==t?(N=Q,U=Q,ce=Ln(),ce!==t?(Se=Ln(),Se!==t?(ht=Ln(),ht!==t?(Bt=Ln(),Bt!==t?(Jr=Ln(),Jr!==t?(hi=Ln(),hi!==t?(rs=Ln(),rs!==t?(Jb=Ln(),Jb!==t?(ce=[ce,Se,ht,Bt,Jr,hi,rs,Jb],U=ce):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t)):(Q=U,U=t),U!==t?N=r.substring(N,Q):N=U,N!==t?(Me=m,b=ag(N),m=b):(Q=m,m=t)):(Q=m,m=t)))),m}function Gb(){var m;return Ag.test(r.charAt(Q))?(m=r.charAt(Q),Q++):(m=t,I===0&&Qe(ca)),m}function Ln(){var m;return ua.test(r.charAt(Q))?(m=r.charAt(Q),Q++):(m=t,I===0&&Qe(rt)),m}function Lue(){var m,b,N,U,ce;if(m=Q,b=[],N=Q,r.charCodeAt(Q)===92?(U=es,Q++):(U=t,I===0&&Qe(gt)),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t),N===t&&(N=Q,U=Q,I++,ce=b1(),I--,ce===t?U=void 0:(Q=U,U=t),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t)),N!==t)for(;N!==t;)b.push(N),N=Q,r.charCodeAt(Q)===92?(U=es,Q++):(U=t,I===0&&Qe(gt)),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t),N===t&&(N=Q,U=Q,I++,ce=b1(),I--,ce===t?U=void 0:(Q=U,U=t),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t));else b=t;return b!==t&&(Me=m,b=Dn(b)),m=b,m}function Yb(){var m,b,N,U,ce,Se;if(m=Q,r.charCodeAt(Q)===45?(b=fA,Q++):(b=t,I===0&&Qe(Pl)),b===t&&(r.charCodeAt(Q)===43?(b=Ms,Q++):(b=t,I===0&&Qe(Dl))),b===t&&(b=null),b!==t){if(N=[],qe.test(r.charAt(Q))?(U=r.charAt(Q),Q++):(U=t,I===0&&Qe(ne)),U!==t)for(;U!==t;)N.push(U),qe.test(r.charAt(Q))?(U=r.charAt(Q),Q++):(U=t,I===0&&Qe(ne));else N=t;if(N!==t)if(r.charCodeAt(Q)===46?(U=vE,Q++):(U=t,I===0&&Qe(yp)),U!==t){if(ce=[],qe.test(r.charAt(Q))?(Se=r.charAt(Q),Q++):(Se=t,I===0&&Qe(ne)),Se!==t)for(;Se!==t;)ce.push(Se),qe.test(r.charAt(Q))?(Se=r.charAt(Q),Q++):(Se=t,I===0&&Qe(ne));else ce=t;ce!==t?(Me=m,b=lg(b,N,ce),m=b):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;if(m===t){if(m=Q,r.charCodeAt(Q)===45?(b=fA,Q++):(b=t,I===0&&Qe(Pl)),b===t&&(r.charCodeAt(Q)===43?(b=Ms,Q++):(b=t,I===0&&Qe(Dl))),b===t&&(b=null),b!==t){if(N=[],qe.test(r.charAt(Q))?(U=r.charAt(Q),Q++):(U=t,I===0&&Qe(ne)),U!==t)for(;U!==t;)N.push(U),qe.test(r.charAt(Q))?(U=r.charAt(Q),Q++):(U=t,I===0&&Qe(ne));else N=t;N!==t?(Me=m,b=wp(b,N),m=b):(Q=m,m=t)}else Q=m,m=t;if(m===t&&(m=Q,b=qb(),b!==t&&(Me=m,b=xE(b)),m=b,m===t&&(m=Q,b=Rl(),b!==t&&(Me=m,b=kl(b)),m=b,m===t)))if(m=Q,r.charCodeAt(Q)===40?(b=ge,Q++):(b=t,I===0&&Qe(re)),b!==t){for(N=[],U=He();U!==t;)N.push(U),U=He();if(N!==t)if(U=y1(),U!==t){for(ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();ce!==t?(r.charCodeAt(Q)===41?(Se=O,Q++):(Se=t,I===0&&Qe(F)),Se!==t?(Me=m,b=PE(U),m=b):(Q=m,m=t)):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t}return m}function jb(){var m,b,N,U,ce,Se,ht,Bt;if(m=Q,b=Yb(),b!==t){for(N=[],U=Q,ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();if(ce!==t)if(r.charCodeAt(Q)===42?(Se=cg,Q++):(Se=t,I===0&&Qe(hA)),Se===t&&(r.charCodeAt(Q)===47?(Se=Rr,Q++):(Se=t,I===0&&Qe(DE))),Se!==t){for(ht=[],Bt=He();Bt!==t;)ht.push(Bt),Bt=He();ht!==t?(Bt=Yb(),Bt!==t?(Me=U,ce=Ks(b,Se,Bt),U=ce):(Q=U,U=t)):(Q=U,U=t)}else Q=U,U=t;else Q=U,U=t;for(;U!==t;){for(N.push(U),U=Q,ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();if(ce!==t)if(r.charCodeAt(Q)===42?(Se=cg,Q++):(Se=t,I===0&&Qe(hA)),Se===t&&(r.charCodeAt(Q)===47?(Se=Rr,Q++):(Se=t,I===0&&Qe(DE))),Se!==t){for(ht=[],Bt=He();Bt!==t;)ht.push(Bt),Bt=He();ht!==t?(Bt=Yb(),Bt!==t?(Me=U,ce=Ks(b,Se,Bt),U=ce):(Q=U,U=t)):(Q=U,U=t)}else Q=U,U=t;else Q=U,U=t}N!==t?(Me=m,b=Us(b,N),m=b):(Q=m,m=t)}else Q=m,m=t;return m}function y1(){var m,b,N,U,ce,Se,ht,Bt;if(m=Q,b=jb(),b!==t){for(N=[],U=Q,ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();if(ce!==t)if(r.charCodeAt(Q)===43?(Se=Ms,Q++):(Se=t,I===0&&Qe(Dl)),Se===t&&(r.charCodeAt(Q)===45?(Se=fA,Q++):(Se=t,I===0&&Qe(Pl))),Se!==t){for(ht=[],Bt=He();Bt!==t;)ht.push(Bt),Bt=He();ht!==t?(Bt=jb(),Bt!==t?(Me=U,ce=ug(b,Se,Bt),U=ce):(Q=U,U=t)):(Q=U,U=t)}else Q=U,U=t;else Q=U,U=t;for(;U!==t;){for(N.push(U),U=Q,ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();if(ce!==t)if(r.charCodeAt(Q)===43?(Se=Ms,Q++):(Se=t,I===0&&Qe(Dl)),Se===t&&(r.charCodeAt(Q)===45?(Se=fA,Q++):(Se=t,I===0&&Qe(Pl))),Se!==t){for(ht=[],Bt=He();Bt!==t;)ht.push(Bt),Bt=He();ht!==t?(Bt=jb(),Bt!==t?(Me=U,ce=ug(b,Se,Bt),U=ce):(Q=U,U=t)):(Q=U,U=t)}else Q=U,U=t;else Q=U,U=t}N!==t?(Me=m,b=Us(b,N),m=b):(Q=m,m=t)}else Q=m,m=t;return m}function w1(){var m,b,N,U,ce,Se;if(m=Q,r.substr(Q,3)===pA?(b=pA,Q+=3):(b=t,I===0&&Qe(R)),b!==t){for(N=[],U=He();U!==t;)N.push(U),U=He();if(N!==t)if(U=y1(),U!==t){for(ce=[],Se=He();Se!==t;)ce.push(Se),Se=He();ce!==t?(r.substr(Q,2)===q?(Se=q,Q+=2):(Se=t,I===0&&Qe(Ce)),Se!==t?(Me=m,b=Ke(U),m=b):(Q=m,m=t)):(Q=m,m=t)}else Q=m,m=t;else Q=m,m=t}else Q=m,m=t;return m}function B1(){var m,b,N,U;return m=Q,r.substr(Q,2)===Re?(b=Re,Q+=2):(b=t,I===0&&Qe(ze)),b!==t?(N=Mr(),N!==t?(r.charCodeAt(Q)===41?(U=O,Q++):(U=t,I===0&&Qe(F)),U!==t?(Me=m,b=dt(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m}function qb(){var m,b,N,U,ce,Se;return m=Q,r.substr(Q,2)===Ft?(b=Ft,Q+=2):(b=t,I===0&&Qe(Fn)),b!==t?(N=Rl(),N!==t?(r.substr(Q,2)===Db?(U=Db,Q+=2):(U=t,I===0&&Qe($M)),U!==t?(ce=f1(),ce!==t?(r.charCodeAt(Q)===125?(Se=Fe,Q++):(Se=t,I===0&&Qe(Ne)),Se!==t?(Me=m,b=e1(N,ce),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===Ft?(b=Ft,Q+=2):(b=t,I===0&&Qe(Fn)),b!==t?(N=Rl(),N!==t?(r.substr(Q,3)===kb?(U=kb,Q+=3):(U=t,I===0&&Qe(t1)),U!==t?(Me=m,b=r1(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===Ft?(b=Ft,Q+=2):(b=t,I===0&&Qe(Fn)),b!==t?(N=Rl(),N!==t?(r.substr(Q,2)===Rb?(U=Rb,Q+=2):(U=t,I===0&&Qe(i1)),U!==t?(ce=f1(),ce!==t?(r.charCodeAt(Q)===125?(Se=Fe,Q++):(Se=t,I===0&&Qe(Ne)),Se!==t?(Me=m,b=n1(N,ce),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===Ft?(b=Ft,Q+=2):(b=t,I===0&&Qe(Fn)),b!==t?(N=Rl(),N!==t?(r.substr(Q,3)===Fb?(U=Fb,Q+=3):(U=t,I===0&&Qe(s1)),U!==t?(Me=m,b=o1(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.substr(Q,2)===Ft?(b=Ft,Q+=2):(b=t,I===0&&Qe(Fn)),b!==t?(N=Rl(),N!==t?(r.charCodeAt(Q)===125?(U=Fe,Q++):(U=t,I===0&&Qe(Ne)),U!==t?(Me=m,b=Nb(N),m=b):(Q=m,m=t)):(Q=m,m=t)):(Q=m,m=t),m===t&&(m=Q,r.charCodeAt(Q)===36?(b=a1,Q++):(b=t,I===0&&Qe(A1)),b!==t?(N=Rl(),N!==t?(Me=m,b=Nb(N),m=b):(Q=m,m=t)):(Q=m,m=t)))))),m}function Tue(){var m,b,N;return m=Q,b=Oue(),b!==t?(Me=Q,N=l1(b),N?N=void 0:N=t,N!==t?(Me=m,b=c1(b),m=b):(Q=m,m=t)):(Q=m,m=t),m}function Oue(){var m,b,N,U,ce;if(m=Q,b=[],N=Q,U=Q,I++,ce=S1(),I--,ce===t?U=void 0:(Q=U,U=t),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t),N!==t)for(;N!==t;)b.push(N),N=Q,U=Q,I++,ce=S1(),I--,ce===t?U=void 0:(Q=U,U=t),U!==t?(r.length>Q?(ce=r.charAt(Q),Q++):(ce=t,I===0&&Qe(Eo)),ce!==t?(Me=N,U=an(ce),N=U):(Q=N,N=t)):(Q=N,N=t);else b=t;return b!==t&&(Me=m,b=Dn(b)),m=b,m}function Q1(){var m,b,N;if(m=Q,b=[],Lb.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Tb)),N!==t)for(;N!==t;)b.push(N),Lb.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Tb));else b=t;return b!==t&&(Me=m,b=Ob()),m=b,m}function Rl(){var m,b,N;if(m=Q,b=[],Mb.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Kb)),N!==t)for(;N!==t;)b.push(N),Mb.test(r.charAt(Q))?(N=r.charAt(Q),Q++):(N=t,I===0&&Qe(Kb));else b=t;return b!==t&&(Me=m,b=Ob()),m=b,m}function b1(){var m;return u1.test(r.charAt(Q))?(m=r.charAt(Q),Q++):(m=t,I===0&&Qe(gg)),m}function S1(){var m;return Ub.test(r.charAt(Q))?(m=r.charAt(Q),Q++):(m=t,I===0&&Qe(Hb)),m}function He(){var m,b;if(m=[],kE.test(r.charAt(Q))?(b=r.charAt(Q),Q++):(b=t,I===0&&Qe(RE)),b!==t)for(;b!==t;)m.push(b),kE.test(r.charAt(Q))?(b=r.charAt(Q),Q++):(b=t,I===0&&Qe(RE));else m=t;return m}if(k=n(),k!==t&&Q===r.length)return k;throw k!==t&&Q{"use strict";function Mge(r,e){function t(){this.constructor=r}t.prototype=e.prototype,r.prototype=new t}function Ul(r,e,t,i){this.message=r,this.expected=e,this.found=t,this.location=i,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,Ul)}Mge(Ul,Error);Ul.buildMessage=function(r,e){var t={literal:function(c){return'"'+n(c.text)+'"'},class:function(c){var u="",g;for(g=0;g0){for(g=1,f=1;gH&&(H=v,j=[]),j.push(ne))}function Ne(ne,Y){return new Ul(ne,null,null,Y)}function oe(ne,Y,pe){return new Ul(Ul.buildMessage(ne,Y),ne,Y,pe)}function le(){var ne,Y,pe,ie;return ne=v,Y=we(),Y!==t?(r.charCodeAt(v)===47?(pe=s,v++):(pe=t,$===0&&Fe(o)),pe!==t?(ie=we(),ie!==t?(D=ne,Y=a(Y,ie),ne=Y):(v=ne,ne=t)):(v=ne,ne=t)):(v=ne,ne=t),ne===t&&(ne=v,Y=we(),Y!==t&&(D=ne,Y=l(Y)),ne=Y),ne}function we(){var ne,Y,pe,ie;return ne=v,Y=fe(),Y!==t?(r.charCodeAt(v)===64?(pe=c,v++):(pe=t,$===0&&Fe(u)),pe!==t?(ie=qe(),ie!==t?(D=ne,Y=g(Y,ie),ne=Y):(v=ne,ne=t)):(v=ne,ne=t)):(v=ne,ne=t),ne===t&&(ne=v,Y=fe(),Y!==t&&(D=ne,Y=f(Y)),ne=Y),ne}function fe(){var ne,Y,pe,ie,de;return ne=v,r.charCodeAt(v)===64?(Y=c,v++):(Y=t,$===0&&Fe(u)),Y!==t?(pe=Ae(),pe!==t?(r.charCodeAt(v)===47?(ie=s,v++):(ie=t,$===0&&Fe(o)),ie!==t?(de=Ae(),de!==t?(D=ne,Y=h(),ne=Y):(v=ne,ne=t)):(v=ne,ne=t)):(v=ne,ne=t)):(v=ne,ne=t),ne===t&&(ne=v,Y=Ae(),Y!==t&&(D=ne,Y=h()),ne=Y),ne}function Ae(){var ne,Y,pe;if(ne=v,Y=[],p.test(r.charAt(v))?(pe=r.charAt(v),v++):(pe=t,$===0&&Fe(C)),pe!==t)for(;pe!==t;)Y.push(pe),p.test(r.charAt(v))?(pe=r.charAt(v),v++):(pe=t,$===0&&Fe(C));else Y=t;return Y!==t&&(D=ne,Y=h()),ne=Y,ne}function qe(){var ne,Y,pe;if(ne=v,Y=[],y.test(r.charAt(v))?(pe=r.charAt(v),v++):(pe=t,$===0&&Fe(B)),pe!==t)for(;pe!==t;)Y.push(pe),y.test(r.charAt(v))?(pe=r.charAt(v),v++):(pe=t,$===0&&Fe(B));else Y=t;return Y!==t&&(D=ne,Y=h()),ne=Y,ne}if(V=n(),V!==t&&v===r.length)return V;throw V!==t&&v{"use strict";function UK(r){return typeof r>"u"||r===null}function Uge(r){return typeof r=="object"&&r!==null}function Hge(r){return Array.isArray(r)?r:UK(r)?[]:[r]}function Gge(r,e){var t,i,n,s;if(e)for(s=Object.keys(e),t=0,i=s.length;t{"use strict";function Op(r,e){Error.call(this),this.name="YAMLException",this.reason=r,this.mark=e,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():""),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}Op.prototype=Object.create(Error.prototype);Op.prototype.constructor=Op;Op.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t};HK.exports=Op});var jK=w((pXe,YK)=>{"use strict";var GK=Gl();function SS(r,e,t,i,n){this.name=r,this.buffer=e,this.position=t,this.line=i,this.column=n}SS.prototype.getSnippet=function(e,t){var i,n,s,o,a;if(!this.buffer)return null;for(e=e||4,t=t||75,i="",n=this.position;n>0&&`\0\r +\x85\u2028\u2029`.indexOf(this.buffer.charAt(n-1))===-1;)if(n-=1,this.position-n>t/2-1){i=" ... ",n+=5;break}for(s="",o=this.position;ot/2-1){s=" ... ",o-=5;break}return a=this.buffer.slice(n,o),GK.repeat(" ",e)+i+a+s+` +`+GK.repeat(" ",e+this.position-n+i.length)+"^"};SS.prototype.toString=function(e){var t,i="";return this.name&&(i+='in "'+this.name+'" '),i+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet(),t&&(i+=`: +`+t)),i};YK.exports=SS});var si=w((dXe,JK)=>{"use strict";var qK=Qg(),qge=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],Jge=["scalar","sequence","mapping"];function Wge(r){var e={};return r!==null&&Object.keys(r).forEach(function(t){r[t].forEach(function(i){e[String(i)]=t})}),e}function zge(r,e){if(e=e||{},Object.keys(e).forEach(function(t){if(qge.indexOf(t)===-1)throw new qK('Unknown option "'+t+'" is met in definition of "'+r+'" YAML type.')}),this.tag=r,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(t){return t},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.defaultStyle=e.defaultStyle||null,this.styleAliases=Wge(e.styleAliases||null),Jge.indexOf(this.kind)===-1)throw new qK('Unknown kind "'+this.kind+'" is specified for "'+r+'" YAML type.')}JK.exports=zge});var Yl=w((CXe,zK)=>{"use strict";var WK=Gl(),nI=Qg(),Vge=si();function vS(r,e,t){var i=[];return r.include.forEach(function(n){t=vS(n,e,t)}),r[e].forEach(function(n){t.forEach(function(s,o){s.tag===n.tag&&s.kind===n.kind&&i.push(o)}),t.push(n)}),t.filter(function(n,s){return i.indexOf(s)===-1})}function Xge(){var r={scalar:{},sequence:{},mapping:{},fallback:{}},e,t;function i(n){r[n.kind][n.tag]=r.fallback[n.tag]=n}for(e=0,t=arguments.length;e{"use strict";var _ge=si();VK.exports=new _ge("tag:yaml.org,2002:str",{kind:"scalar",construct:function(r){return r!==null?r:""}})});var ZK=w((EXe,_K)=>{"use strict";var Zge=si();_K.exports=new Zge("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(r){return r!==null?r:[]}})});var eU=w((IXe,$K)=>{"use strict";var $ge=si();$K.exports=new $ge("tag:yaml.org,2002:map",{kind:"mapping",construct:function(r){return r!==null?r:{}}})});var sI=w((yXe,tU)=>{"use strict";var efe=Yl();tU.exports=new efe({explicit:[XK(),ZK(),eU()]})});var iU=w((wXe,rU)=>{"use strict";var tfe=si();function rfe(r){if(r===null)return!0;var e=r.length;return e===1&&r==="~"||e===4&&(r==="null"||r==="Null"||r==="NULL")}function ife(){return null}function nfe(r){return r===null}rU.exports=new tfe("tag:yaml.org,2002:null",{kind:"scalar",resolve:rfe,construct:ife,predicate:nfe,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})});var sU=w((BXe,nU)=>{"use strict";var sfe=si();function ofe(r){if(r===null)return!1;var e=r.length;return e===4&&(r==="true"||r==="True"||r==="TRUE")||e===5&&(r==="false"||r==="False"||r==="FALSE")}function afe(r){return r==="true"||r==="True"||r==="TRUE"}function Afe(r){return Object.prototype.toString.call(r)==="[object Boolean]"}nU.exports=new sfe("tag:yaml.org,2002:bool",{kind:"scalar",resolve:ofe,construct:afe,predicate:Afe,represent:{lowercase:function(r){return r?"true":"false"},uppercase:function(r){return r?"TRUE":"FALSE"},camelcase:function(r){return r?"True":"False"}},defaultStyle:"lowercase"})});var aU=w((QXe,oU)=>{"use strict";var lfe=Gl(),cfe=si();function ufe(r){return 48<=r&&r<=57||65<=r&&r<=70||97<=r&&r<=102}function gfe(r){return 48<=r&&r<=55}function ffe(r){return 48<=r&&r<=57}function hfe(r){if(r===null)return!1;var e=r.length,t=0,i=!1,n;if(!e)return!1;if(n=r[t],(n==="-"||n==="+")&&(n=r[++t]),n==="0"){if(t+1===e)return!0;if(n=r[++t],n==="b"){for(t++;t=0?"0b"+r.toString(2):"-0b"+r.toString(2).slice(1)},octal:function(r){return r>=0?"0"+r.toString(8):"-0"+r.toString(8).slice(1)},decimal:function(r){return r.toString(10)},hexadecimal:function(r){return r>=0?"0x"+r.toString(16).toUpperCase():"-0x"+r.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})});var cU=w((bXe,lU)=>{"use strict";var AU=Gl(),Cfe=si(),mfe=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function Efe(r){return!(r===null||!mfe.test(r)||r[r.length-1]==="_")}function Ife(r){var e,t,i,n;return e=r.replace(/_/g,"").toLowerCase(),t=e[0]==="-"?-1:1,n=[],"+-".indexOf(e[0])>=0&&(e=e.slice(1)),e===".inf"?t===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:e===".nan"?NaN:e.indexOf(":")>=0?(e.split(":").forEach(function(s){n.unshift(parseFloat(s,10))}),e=0,i=1,n.forEach(function(s){e+=s*i,i*=60}),t*e):t*parseFloat(e,10)}var yfe=/^[-+]?[0-9]+e/;function wfe(r,e){var t;if(isNaN(r))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===r)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===r)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(AU.isNegativeZero(r))return"-0.0";return t=r.toString(10),yfe.test(t)?t.replace("e",".e"):t}function Bfe(r){return Object.prototype.toString.call(r)==="[object Number]"&&(r%1!==0||AU.isNegativeZero(r))}lU.exports=new Cfe("tag:yaml.org,2002:float",{kind:"scalar",resolve:Efe,construct:Ife,predicate:Bfe,represent:wfe,defaultStyle:"lowercase"})});var xS=w((SXe,uU)=>{"use strict";var Qfe=Yl();uU.exports=new Qfe({include:[sI()],implicit:[iU(),sU(),aU(),cU()]})});var PS=w((vXe,gU)=>{"use strict";var bfe=Yl();gU.exports=new bfe({include:[xS()]})});var dU=w((xXe,pU)=>{"use strict";var Sfe=si(),fU=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),hU=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");function vfe(r){return r===null?!1:fU.exec(r)!==null||hU.exec(r)!==null}function xfe(r){var e,t,i,n,s,o,a,l=0,c=null,u,g,f;if(e=fU.exec(r),e===null&&(e=hU.exec(r)),e===null)throw new Error("Date resolve error");if(t=+e[1],i=+e[2]-1,n=+e[3],!e[4])return new Date(Date.UTC(t,i,n));if(s=+e[4],o=+e[5],a=+e[6],e[7]){for(l=e[7].slice(0,3);l.length<3;)l+="0";l=+l}return e[9]&&(u=+e[10],g=+(e[11]||0),c=(u*60+g)*6e4,e[9]==="-"&&(c=-c)),f=new Date(Date.UTC(t,i,n,s,o,a,l)),c&&f.setTime(f.getTime()-c),f}function Pfe(r){return r.toISOString()}pU.exports=new Sfe("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:vfe,construct:xfe,instanceOf:Date,represent:Pfe})});var mU=w((PXe,CU)=>{"use strict";var Dfe=si();function kfe(r){return r==="<<"||r===null}CU.exports=new Dfe("tag:yaml.org,2002:merge",{kind:"scalar",resolve:kfe})});var yU=w((DXe,IU)=>{"use strict";var jl;try{EU=J,jl=EU("buffer").Buffer}catch{}var EU,Rfe=si(),DS=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= +\r`;function Ffe(r){if(r===null)return!1;var e,t,i=0,n=r.length,s=DS;for(t=0;t64)){if(e<0)return!1;i+=6}return i%8===0}function Nfe(r){var e,t,i=r.replace(/[\r\n=]/g,""),n=i.length,s=DS,o=0,a=[];for(e=0;e>16&255),a.push(o>>8&255),a.push(o&255)),o=o<<6|s.indexOf(i.charAt(e));return t=n%4*6,t===0?(a.push(o>>16&255),a.push(o>>8&255),a.push(o&255)):t===18?(a.push(o>>10&255),a.push(o>>2&255)):t===12&&a.push(o>>4&255),jl?jl.from?jl.from(a):new jl(a):a}function Lfe(r){var e="",t=0,i,n,s=r.length,o=DS;for(i=0;i>18&63],e+=o[t>>12&63],e+=o[t>>6&63],e+=o[t&63]),t=(t<<8)+r[i];return n=s%3,n===0?(e+=o[t>>18&63],e+=o[t>>12&63],e+=o[t>>6&63],e+=o[t&63]):n===2?(e+=o[t>>10&63],e+=o[t>>4&63],e+=o[t<<2&63],e+=o[64]):n===1&&(e+=o[t>>2&63],e+=o[t<<4&63],e+=o[64],e+=o[64]),e}function Tfe(r){return jl&&jl.isBuffer(r)}IU.exports=new Rfe("tag:yaml.org,2002:binary",{kind:"scalar",resolve:Ffe,construct:Nfe,predicate:Tfe,represent:Lfe})});var BU=w((kXe,wU)=>{"use strict";var Ofe=si(),Mfe=Object.prototype.hasOwnProperty,Kfe=Object.prototype.toString;function Ufe(r){if(r===null)return!0;var e=[],t,i,n,s,o,a=r;for(t=0,i=a.length;t{"use strict";var Gfe=si(),Yfe=Object.prototype.toString;function jfe(r){if(r===null)return!0;var e,t,i,n,s,o=r;for(s=new Array(o.length),e=0,t=o.length;e{"use strict";var Jfe=si(),Wfe=Object.prototype.hasOwnProperty;function zfe(r){if(r===null)return!0;var e,t=r;for(e in t)if(Wfe.call(t,e)&&t[e]!==null)return!1;return!0}function Vfe(r){return r!==null?r:{}}SU.exports=new Jfe("tag:yaml.org,2002:set",{kind:"mapping",resolve:zfe,construct:Vfe})});var Sg=w((NXe,xU)=>{"use strict";var Xfe=Yl();xU.exports=new Xfe({include:[PS()],implicit:[dU(),mU()],explicit:[yU(),BU(),bU(),vU()]})});var DU=w((LXe,PU)=>{"use strict";var _fe=si();function Zfe(){return!0}function $fe(){}function ehe(){return""}function the(r){return typeof r>"u"}PU.exports=new _fe("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:Zfe,construct:$fe,predicate:the,represent:ehe})});var RU=w((TXe,kU)=>{"use strict";var rhe=si();function ihe(r){if(r===null||r.length===0)return!1;var e=r,t=/\/([gim]*)$/.exec(r),i="";return!(e[0]==="/"&&(t&&(i=t[1]),i.length>3||e[e.length-i.length-1]!=="/"))}function nhe(r){var e=r,t=/\/([gim]*)$/.exec(r),i="";return e[0]==="/"&&(t&&(i=t[1]),e=e.slice(1,e.length-i.length-1)),new RegExp(e,i)}function she(r){var e="/"+r.source+"/";return r.global&&(e+="g"),r.multiline&&(e+="m"),r.ignoreCase&&(e+="i"),e}function ohe(r){return Object.prototype.toString.call(r)==="[object RegExp]"}kU.exports=new rhe("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:ihe,construct:nhe,predicate:ohe,represent:she})});var LU=w((OXe,NU)=>{"use strict";var oI;try{FU=J,oI=FU("esprima")}catch{typeof window<"u"&&(oI=window.esprima)}var FU,ahe=si();function Ahe(r){if(r===null)return!1;try{var e="("+r+")",t=oI.parse(e,{range:!0});return!(t.type!=="Program"||t.body.length!==1||t.body[0].type!=="ExpressionStatement"||t.body[0].expression.type!=="ArrowFunctionExpression"&&t.body[0].expression.type!=="FunctionExpression")}catch{return!1}}function lhe(r){var e="("+r+")",t=oI.parse(e,{range:!0}),i=[],n;if(t.type!=="Program"||t.body.length!==1||t.body[0].type!=="ExpressionStatement"||t.body[0].expression.type!=="ArrowFunctionExpression"&&t.body[0].expression.type!=="FunctionExpression")throw new Error("Failed to resolve function");return t.body[0].expression.params.forEach(function(s){i.push(s.name)}),n=t.body[0].expression.body.range,t.body[0].expression.body.type==="BlockStatement"?new Function(i,e.slice(n[0]+1,n[1]-1)):new Function(i,"return "+e.slice(n[0],n[1]))}function che(r){return r.toString()}function uhe(r){return Object.prototype.toString.call(r)==="[object Function]"}NU.exports=new ahe("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:Ahe,construct:lhe,predicate:uhe,represent:che})});var Mp=w((MXe,OU)=>{"use strict";var TU=Yl();OU.exports=TU.DEFAULT=new TU({include:[Sg()],explicit:[DU(),RU(),LU()]})});var r2=w((KXe,Kp)=>{"use strict";var da=Gl(),jU=Qg(),ghe=jK(),qU=Sg(),fhe=Mp(),wA=Object.prototype.hasOwnProperty,aI=1,JU=2,WU=3,AI=4,kS=1,hhe=2,MU=3,phe=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,dhe=/[\x85\u2028\u2029]/,Che=/[,\[\]\{\}]/,zU=/^(?:!|!!|![a-z\-]+!)$/i,VU=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function KU(r){return Object.prototype.toString.call(r)}function Bo(r){return r===10||r===13}function Jl(r){return r===9||r===32}function un(r){return r===9||r===32||r===10||r===13}function vg(r){return r===44||r===91||r===93||r===123||r===125}function mhe(r){var e;return 48<=r&&r<=57?r-48:(e=r|32,97<=e&&e<=102?e-97+10:-1)}function Ehe(r){return r===120?2:r===117?4:r===85?8:0}function Ihe(r){return 48<=r&&r<=57?r-48:-1}function UU(r){return r===48?"\0":r===97?"\x07":r===98?"\b":r===116||r===9?" ":r===110?` +`:r===118?"\v":r===102?"\f":r===114?"\r":r===101?"\x1B":r===32?" ":r===34?'"':r===47?"/":r===92?"\\":r===78?"\x85":r===95?"\xA0":r===76?"\u2028":r===80?"\u2029":""}function yhe(r){return r<=65535?String.fromCharCode(r):String.fromCharCode((r-65536>>10)+55296,(r-65536&1023)+56320)}var XU=new Array(256),_U=new Array(256);for(ql=0;ql<256;ql++)XU[ql]=UU(ql)?1:0,_U[ql]=UU(ql);var ql;function whe(r,e){this.input=r,this.filename=e.filename||null,this.schema=e.schema||fhe,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.json=e.json||!1,this.listener=e.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=r.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function ZU(r,e){return new jU(e,new ghe(r.filename,r.input,r.position,r.line,r.position-r.lineStart))}function ft(r,e){throw ZU(r,e)}function lI(r,e){r.onWarning&&r.onWarning.call(null,ZU(r,e))}var HU={YAML:function(e,t,i){var n,s,o;e.version!==null&&ft(e,"duplication of %YAML directive"),i.length!==1&&ft(e,"YAML directive accepts exactly one argument"),n=/^([0-9]+)\.([0-9]+)$/.exec(i[0]),n===null&&ft(e,"ill-formed argument of the YAML directive"),s=parseInt(n[1],10),o=parseInt(n[2],10),s!==1&&ft(e,"unacceptable YAML version of the document"),e.version=i[0],e.checkLineBreaks=o<2,o!==1&&o!==2&&lI(e,"unsupported YAML version of the document")},TAG:function(e,t,i){var n,s;i.length!==2&&ft(e,"TAG directive accepts exactly two arguments"),n=i[0],s=i[1],zU.test(n)||ft(e,"ill-formed tag handle (first argument) of the TAG directive"),wA.call(e.tagMap,n)&&ft(e,'there is a previously declared suffix for "'+n+'" tag handle'),VU.test(s)||ft(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[n]=s}};function yA(r,e,t,i){var n,s,o,a;if(e1&&(r.result+=da.repeat(` +`,e-1))}function Bhe(r,e,t){var i,n,s,o,a,l,c,u,g=r.kind,f=r.result,h;if(h=r.input.charCodeAt(r.position),un(h)||vg(h)||h===35||h===38||h===42||h===33||h===124||h===62||h===39||h===34||h===37||h===64||h===96||(h===63||h===45)&&(n=r.input.charCodeAt(r.position+1),un(n)||t&&vg(n)))return!1;for(r.kind="scalar",r.result="",s=o=r.position,a=!1;h!==0;){if(h===58){if(n=r.input.charCodeAt(r.position+1),un(n)||t&&vg(n))break}else if(h===35){if(i=r.input.charCodeAt(r.position-1),un(i))break}else{if(r.position===r.lineStart&&cI(r)||t&&vg(h))break;if(Bo(h))if(l=r.line,c=r.lineStart,u=r.lineIndent,zr(r,!1,-1),r.lineIndent>=e){a=!0,h=r.input.charCodeAt(r.position);continue}else{r.position=o,r.line=l,r.lineStart=c,r.lineIndent=u;break}}a&&(yA(r,s,o,!1),FS(r,r.line-l),s=o=r.position,a=!1),Jl(h)||(o=r.position+1),h=r.input.charCodeAt(++r.position)}return yA(r,s,o,!1),r.result?!0:(r.kind=g,r.result=f,!1)}function Qhe(r,e){var t,i,n;if(t=r.input.charCodeAt(r.position),t!==39)return!1;for(r.kind="scalar",r.result="",r.position++,i=n=r.position;(t=r.input.charCodeAt(r.position))!==0;)if(t===39)if(yA(r,i,r.position,!0),t=r.input.charCodeAt(++r.position),t===39)i=r.position,r.position++,n=r.position;else return!0;else Bo(t)?(yA(r,i,n,!0),FS(r,zr(r,!1,e)),i=n=r.position):r.position===r.lineStart&&cI(r)?ft(r,"unexpected end of the document within a single quoted scalar"):(r.position++,n=r.position);ft(r,"unexpected end of the stream within a single quoted scalar")}function bhe(r,e){var t,i,n,s,o,a;if(a=r.input.charCodeAt(r.position),a!==34)return!1;for(r.kind="scalar",r.result="",r.position++,t=i=r.position;(a=r.input.charCodeAt(r.position))!==0;){if(a===34)return yA(r,t,r.position,!0),r.position++,!0;if(a===92){if(yA(r,t,r.position,!0),a=r.input.charCodeAt(++r.position),Bo(a))zr(r,!1,e);else if(a<256&&XU[a])r.result+=_U[a],r.position++;else if((o=Ehe(a))>0){for(n=o,s=0;n>0;n--)a=r.input.charCodeAt(++r.position),(o=mhe(a))>=0?s=(s<<4)+o:ft(r,"expected hexadecimal character");r.result+=yhe(s),r.position++}else ft(r,"unknown escape sequence");t=i=r.position}else Bo(a)?(yA(r,t,i,!0),FS(r,zr(r,!1,e)),t=i=r.position):r.position===r.lineStart&&cI(r)?ft(r,"unexpected end of the document within a double quoted scalar"):(r.position++,i=r.position)}ft(r,"unexpected end of the stream within a double quoted scalar")}function She(r,e){var t=!0,i,n=r.tag,s,o=r.anchor,a,l,c,u,g,f={},h,p,C,y;if(y=r.input.charCodeAt(r.position),y===91)l=93,g=!1,s=[];else if(y===123)l=125,g=!0,s={};else return!1;for(r.anchor!==null&&(r.anchorMap[r.anchor]=s),y=r.input.charCodeAt(++r.position);y!==0;){if(zr(r,!0,e),y=r.input.charCodeAt(r.position),y===l)return r.position++,r.tag=n,r.anchor=o,r.kind=g?"mapping":"sequence",r.result=s,!0;t||ft(r,"missed comma between flow collection entries"),p=h=C=null,c=u=!1,y===63&&(a=r.input.charCodeAt(r.position+1),un(a)&&(c=u=!0,r.position++,zr(r,!0,e))),i=r.line,Pg(r,e,aI,!1,!0),p=r.tag,h=r.result,zr(r,!0,e),y=r.input.charCodeAt(r.position),(u||r.line===i)&&y===58&&(c=!0,y=r.input.charCodeAt(++r.position),zr(r,!0,e),Pg(r,e,aI,!1,!0),C=r.result),g?xg(r,s,f,p,h,C):c?s.push(xg(r,null,f,p,h,C)):s.push(h),zr(r,!0,e),y=r.input.charCodeAt(r.position),y===44?(t=!0,y=r.input.charCodeAt(++r.position)):t=!1}ft(r,"unexpected end of the stream within a flow collection")}function vhe(r,e){var t,i,n=kS,s=!1,o=!1,a=e,l=0,c=!1,u,g;if(g=r.input.charCodeAt(r.position),g===124)i=!1;else if(g===62)i=!0;else return!1;for(r.kind="scalar",r.result="";g!==0;)if(g=r.input.charCodeAt(++r.position),g===43||g===45)kS===n?n=g===43?MU:hhe:ft(r,"repeat of a chomping mode identifier");else if((u=Ihe(g))>=0)u===0?ft(r,"bad explicit indentation width of a block scalar; it cannot be less than one"):o?ft(r,"repeat of an indentation width identifier"):(a=e+u-1,o=!0);else break;if(Jl(g)){do g=r.input.charCodeAt(++r.position);while(Jl(g));if(g===35)do g=r.input.charCodeAt(++r.position);while(!Bo(g)&&g!==0)}for(;g!==0;){for(RS(r),r.lineIndent=0,g=r.input.charCodeAt(r.position);(!o||r.lineIndenta&&(a=r.lineIndent),Bo(g)){l++;continue}if(r.lineIndente)&&l!==0)ft(r,"bad indentation of a sequence entry");else if(r.lineIndente)&&(Pg(r,e,AI,!0,n)&&(p?f=r.result:h=r.result),p||(xg(r,c,u,g,f,h,s,o),g=f=h=null),zr(r,!0,-1),y=r.input.charCodeAt(r.position)),r.lineIndent>e&&y!==0)ft(r,"bad indentation of a mapping entry");else if(r.lineIndente?l=1:r.lineIndent===e?l=0:r.lineIndente?l=1:r.lineIndent===e?l=0:r.lineIndent tag; it should be "scalar", not "'+r.kind+'"'),g=0,f=r.implicitTypes.length;g tag; it should be "'+h.kind+'", not "'+r.kind+'"'),h.resolve(r.result)?(r.result=h.construct(r.result),r.anchor!==null&&(r.anchorMap[r.anchor]=r.result)):ft(r,"cannot resolve a node with !<"+r.tag+"> explicit tag")):ft(r,"unknown tag !<"+r.tag+">");return r.listener!==null&&r.listener("close",r),r.tag!==null||r.anchor!==null||u}function Rhe(r){var e=r.position,t,i,n,s=!1,o;for(r.version=null,r.checkLineBreaks=r.legacy,r.tagMap={},r.anchorMap={};(o=r.input.charCodeAt(r.position))!==0&&(zr(r,!0,-1),o=r.input.charCodeAt(r.position),!(r.lineIndent>0||o!==37));){for(s=!0,o=r.input.charCodeAt(++r.position),t=r.position;o!==0&&!un(o);)o=r.input.charCodeAt(++r.position);for(i=r.input.slice(t,r.position),n=[],i.length<1&&ft(r,"directive name must not be less than one character in length");o!==0;){for(;Jl(o);)o=r.input.charCodeAt(++r.position);if(o===35){do o=r.input.charCodeAt(++r.position);while(o!==0&&!Bo(o));break}if(Bo(o))break;for(t=r.position;o!==0&&!un(o);)o=r.input.charCodeAt(++r.position);n.push(r.input.slice(t,r.position))}o!==0&&RS(r),wA.call(HU,i)?HU[i](r,i,n):lI(r,'unknown document directive "'+i+'"')}if(zr(r,!0,-1),r.lineIndent===0&&r.input.charCodeAt(r.position)===45&&r.input.charCodeAt(r.position+1)===45&&r.input.charCodeAt(r.position+2)===45?(r.position+=3,zr(r,!0,-1)):s&&ft(r,"directives end mark is expected"),Pg(r,r.lineIndent-1,AI,!1,!0),zr(r,!0,-1),r.checkLineBreaks&&dhe.test(r.input.slice(e,r.position))&&lI(r,"non-ASCII line breaks are interpreted as content"),r.documents.push(r.result),r.position===r.lineStart&&cI(r)){r.input.charCodeAt(r.position)===46&&(r.position+=3,zr(r,!0,-1));return}if(r.position"u"&&(t=e,e=null);var i=$U(r,t);if(typeof e!="function")return i;for(var n=0,s=i.length;n"u"&&(t=e,e=null),e2(r,e,da.extend({schema:qU},t))}function Nhe(r,e){return t2(r,da.extend({schema:qU},e))}Kp.exports.loadAll=e2;Kp.exports.load=t2;Kp.exports.safeLoadAll=Fhe;Kp.exports.safeLoad=Nhe});var b2=w((UXe,OS)=>{"use strict";var Hp=Gl(),Gp=Qg(),Lhe=Mp(),The=Sg(),c2=Object.prototype.toString,u2=Object.prototype.hasOwnProperty,Ohe=9,Up=10,Mhe=13,Khe=32,Uhe=33,Hhe=34,g2=35,Ghe=37,Yhe=38,jhe=39,qhe=42,f2=44,Jhe=45,h2=58,Whe=61,zhe=62,Vhe=63,Xhe=64,p2=91,d2=93,_he=96,C2=123,Zhe=124,m2=125,Fi={};Fi[0]="\\0";Fi[7]="\\a";Fi[8]="\\b";Fi[9]="\\t";Fi[10]="\\n";Fi[11]="\\v";Fi[12]="\\f";Fi[13]="\\r";Fi[27]="\\e";Fi[34]='\\"';Fi[92]="\\\\";Fi[133]="\\N";Fi[160]="\\_";Fi[8232]="\\L";Fi[8233]="\\P";var $he=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];function epe(r,e){var t,i,n,s,o,a,l;if(e===null)return{};for(t={},i=Object.keys(e),n=0,s=i.length;n0?r.charCodeAt(s-1):null,f=f&&s2(o,a)}else{for(s=0;si&&r[g+1]!==" ",g=s);else if(!Dg(o))return uI;a=s>0?r.charCodeAt(s-1):null,f=f&&s2(o,a)}c=c||u&&s-g-1>i&&r[g+1]!==" "}return!l&&!c?f&&!n(r)?I2:y2:t>9&&E2(r)?uI:c?B2:w2}function ope(r,e,t,i){r.dump=function(){if(e.length===0)return"''";if(!r.noCompatMode&&$he.indexOf(e)!==-1)return"'"+e+"'";var n=r.indent*Math.max(1,t),s=r.lineWidth===-1?-1:Math.max(Math.min(r.lineWidth,40),r.lineWidth-n),o=i||r.flowLevel>-1&&t>=r.flowLevel;function a(l){return rpe(r,l)}switch(spe(e,o,r.indent,s,a)){case I2:return e;case y2:return"'"+e.replace(/'/g,"''")+"'";case w2:return"|"+o2(e,r.indent)+a2(n2(e,n));case B2:return">"+o2(e,r.indent)+a2(n2(ape(e,s),n));case uI:return'"'+Ape(e,s)+'"';default:throw new Gp("impossible error: invalid scalar style")}}()}function o2(r,e){var t=E2(r)?String(e):"",i=r[r.length-1]===` +`,n=i&&(r[r.length-2]===` +`||r===` +`),s=n?"+":i?"":"-";return t+s+` +`}function a2(r){return r[r.length-1]===` +`?r.slice(0,-1):r}function ape(r,e){for(var t=/(\n+)([^\n]*)/g,i=function(){var c=r.indexOf(` +`);return c=c!==-1?c:r.length,t.lastIndex=c,A2(r.slice(0,c),e)}(),n=r[0]===` +`||r[0]===" ",s,o;o=t.exec(r);){var a=o[1],l=o[2];s=l[0]===" ",i+=a+(!n&&!s&&l!==""?` +`:"")+A2(l,e),n=s}return i}function A2(r,e){if(r===""||r[0]===" ")return r;for(var t=/ [^ ]/g,i,n=0,s,o=0,a=0,l="";i=t.exec(r);)a=i.index,a-n>e&&(s=o>n?o:a,l+=` +`+r.slice(n,s),n=s+1),o=a;return l+=` +`,r.length-n>e&&o>n?l+=r.slice(n,o)+` +`+r.slice(o+1):l+=r.slice(n),l.slice(1)}function Ape(r){for(var e="",t,i,n,s=0;s=55296&&t<=56319&&(i=r.charCodeAt(s+1),i>=56320&&i<=57343)){e+=i2((t-55296)*1024+i-56320+65536),s++;continue}n=Fi[t],e+=!n&&Dg(t)?r[s]:n||i2(t)}return e}function lpe(r,e,t){var i="",n=r.tag,s,o;for(s=0,o=t.length;s1024&&(u+="? "),u+=r.dump+(r.condenseFlow?'"':"")+":"+(r.condenseFlow?"":" "),Wl(r,e,c,!1,!1)&&(u+=r.dump,i+=u));r.tag=n,r.dump="{"+i+"}"}function gpe(r,e,t,i){var n="",s=r.tag,o=Object.keys(t),a,l,c,u,g,f;if(r.sortKeys===!0)o.sort();else if(typeof r.sortKeys=="function")o.sort(r.sortKeys);else if(r.sortKeys)throw new Gp("sortKeys must be a boolean or a function");for(a=0,l=o.length;a1024,g&&(r.dump&&Up===r.dump.charCodeAt(0)?f+="?":f+="? "),f+=r.dump,g&&(f+=NS(r,e)),Wl(r,e+1,u,!0,g)&&(r.dump&&Up===r.dump.charCodeAt(0)?f+=":":f+=": ",f+=r.dump,n+=f));r.tag=s,r.dump=n||"{}"}function l2(r,e,t){var i,n,s,o,a,l;for(n=t?r.explicitTypes:r.implicitTypes,s=0,o=n.length;s tag resolver accepts not "'+l+'" style');r.dump=i}return!0}return!1}function Wl(r,e,t,i,n,s){r.tag=null,r.dump=t,l2(r,t,!1)||l2(r,t,!0);var o=c2.call(r.dump);i&&(i=r.flowLevel<0||r.flowLevel>e);var a=o==="[object Object]"||o==="[object Array]",l,c;if(a&&(l=r.duplicates.indexOf(t),c=l!==-1),(r.tag!==null&&r.tag!=="?"||c||r.indent!==2&&e>0)&&(n=!1),c&&r.usedDuplicates[l])r.dump="*ref_"+l;else{if(a&&c&&!r.usedDuplicates[l]&&(r.usedDuplicates[l]=!0),o==="[object Object]")i&&Object.keys(r.dump).length!==0?(gpe(r,e,r.dump,n),c&&(r.dump="&ref_"+l+r.dump)):(upe(r,e,r.dump),c&&(r.dump="&ref_"+l+" "+r.dump));else if(o==="[object Array]"){var u=r.noArrayIndent&&e>0?e-1:e;i&&r.dump.length!==0?(cpe(r,u,r.dump,n),c&&(r.dump="&ref_"+l+r.dump)):(lpe(r,u,r.dump),c&&(r.dump="&ref_"+l+" "+r.dump))}else if(o==="[object String]")r.tag!=="?"&&ope(r,r.dump,e,s);else{if(r.skipInvalid)return!1;throw new Gp("unacceptable kind of an object to dump "+o)}r.tag!==null&&r.tag!=="?"&&(r.dump="!<"+r.tag+"> "+r.dump)}return!0}function fpe(r,e){var t=[],i=[],n,s;for(LS(r,t,i),n=0,s=i.length;n{"use strict";var gI=r2(),S2=b2();function fI(r){return function(){throw new Error("Function "+r+" is deprecated and cannot be used.")}}Fr.exports.Type=si();Fr.exports.Schema=Yl();Fr.exports.FAILSAFE_SCHEMA=sI();Fr.exports.JSON_SCHEMA=xS();Fr.exports.CORE_SCHEMA=PS();Fr.exports.DEFAULT_SAFE_SCHEMA=Sg();Fr.exports.DEFAULT_FULL_SCHEMA=Mp();Fr.exports.load=gI.load;Fr.exports.loadAll=gI.loadAll;Fr.exports.safeLoad=gI.safeLoad;Fr.exports.safeLoadAll=gI.safeLoadAll;Fr.exports.dump=S2.dump;Fr.exports.safeDump=S2.safeDump;Fr.exports.YAMLException=Qg();Fr.exports.MINIMAL_SCHEMA=sI();Fr.exports.SAFE_SCHEMA=Sg();Fr.exports.DEFAULT_SCHEMA=Mp();Fr.exports.scan=fI("scan");Fr.exports.parse=fI("parse");Fr.exports.compose=fI("compose");Fr.exports.addConstructor=fI("addConstructor")});var P2=w((GXe,x2)=>{"use strict";var ppe=v2();x2.exports=ppe});var k2=w((YXe,D2)=>{"use strict";function dpe(r,e){function t(){this.constructor=r}t.prototype=e.prototype,r.prototype=new t}function zl(r,e,t,i){this.message=r,this.expected=e,this.found=t,this.location=i,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,zl)}dpe(zl,Error);zl.buildMessage=function(r,e){var t={literal:function(c){return'"'+n(c.text)+'"'},class:function(c){var u="",g;for(g=0;g0){for(g=1,f=1;g({[Ke]:Ce})))},H=function(R){return R},j=function(R){return R},$=Ts("correct indentation"),V=" ",W=ar(" ",!1),Z=function(R){return R.length===pA*ug},A=function(R){return R.length===(pA+1)*ug},ae=function(){return pA++,!0},ge=function(){return pA--,!0},re=function(){return sg()},O=Ts("pseudostring"),F=/^[^\r\n\t ?:,\][{}#&*!|>'"%@`\-]/,ue=Rn(["\r",` +`," "," ","?",":",",","]","[","{","}","#","&","*","!","|",">","'",'"',"%","@","`","-"],!0,!1),he=/^[^\r\n\t ,\][{}:#"']/,ke=Rn(["\r",` +`," "," ",",","]","[","{","}",":","#",'"',"'"],!0,!1),Fe=function(){return sg().replace(/^ *| *$/g,"")},Ne="--",oe=ar("--",!1),le=/^[a-zA-Z\/0-9]/,we=Rn([["a","z"],["A","Z"],"/",["0","9"]],!1,!1),fe=/^[^\r\n\t :,]/,Ae=Rn(["\r",` +`," "," ",":",","],!0,!1),qe="null",ne=ar("null",!1),Y=function(){return null},pe="true",ie=ar("true",!1),de=function(){return!0},tt="false",Pt=ar("false",!1),It=function(){return!1},Or=Ts("string"),ii='"',gi=ar('"',!1),hr=function(){return""},fi=function(R){return R},ni=function(R){return R.join("")},Ls=/^[^"\\\0-\x1F\x7F]/,pr=Rn(['"',"\\",["\0",""],"\x7F"],!0,!1),Ei='\\"',_n=ar('\\"',!1),oa=function(){return'"'},aA="\\\\",eg=ar("\\\\",!1),Zn=function(){return"\\"},AA="\\/",aa=ar("\\/",!1),up=function(){return"/"},lA="\\b",cA=ar("\\b",!1),wr=function(){return"\b"},wl="\\f",tg=ar("\\f",!1),po=function(){return"\f"},rg="\\n",gp=ar("\\n",!1),fp=function(){return` +`},vr="\\r",se=ar("\\r",!1),Co=function(){return"\r"},Dn="\\t",ig=ar("\\t",!1),Qt=function(){return" "},Bl="\\u",kn=ar("\\u",!1),$n=function(R,q,Ce,Ke){return String.fromCharCode(parseInt(`0x${R}${q}${Ce}${Ke}`))},es=/^[0-9a-fA-F]/,gt=Rn([["0","9"],["a","f"],["A","F"]],!1,!1),mo=Ts("blank space"),At=/^[ \t]/,an=Rn([" "," "],!1,!1),S=Ts("white space"),Tt=/^[ \t\n\r]/,ng=Rn([" "," ",` +`,"\r"],!1,!1),Ql=`\r +`,hp=ar(`\r +`,!1),pp=` +`,dp=ar(` +`,!1),Cp="\r",mp=ar("\r",!1),G=0,yt=0,uA=[{line:1,column:1}],ji=0,bl=[],Xe=0,Aa;if("startRule"in e){if(!(e.startRule in i))throw new Error(`Can't start parsing from rule "`+e.startRule+'".');n=i[e.startRule]}function sg(){return r.substring(yt,G)}function bE(){return An(yt,G)}function Ep(R,q){throw q=q!==void 0?q:An(yt,G),vl([Ts(R)],r.substring(yt,G),q)}function SE(R,q){throw q=q!==void 0?q:An(yt,G),og(R,q)}function ar(R,q){return{type:"literal",text:R,ignoreCase:q}}function Rn(R,q,Ce){return{type:"class",parts:R,inverted:q,ignoreCase:Ce}}function Sl(){return{type:"any"}}function Ip(){return{type:"end"}}function Ts(R){return{type:"other",description:R}}function la(R){var q=uA[R],Ce;if(q)return q;for(Ce=R-1;!uA[Ce];)Ce--;for(q=uA[Ce],q={line:q.line,column:q.column};Ceji&&(ji=G,bl=[]),bl.push(R))}function og(R,q){return new zl(R,null,null,q)}function vl(R,q,Ce){return new zl(zl.buildMessage(R,q),R,q,Ce)}function Os(){var R;return R=ag(),R}function xl(){var R,q,Ce;for(R=G,q=[],Ce=gA();Ce!==t;)q.push(Ce),Ce=gA();return q!==t&&(yt=R,q=s(q)),R=q,R}function gA(){var R,q,Ce,Ke,Re;return R=G,q=ua(),q!==t?(r.charCodeAt(G)===45?(Ce=o,G++):(Ce=t,Xe===0&&Te(a)),Ce!==t?(Ke=Rr(),Ke!==t?(Re=ca(),Re!==t?(yt=R,q=l(Re),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t),R}function ag(){var R,q,Ce;for(R=G,q=[],Ce=Ag();Ce!==t;)q.push(Ce),Ce=Ag();return q!==t&&(yt=R,q=c(q)),R=q,R}function Ag(){var R,q,Ce,Ke,Re,ze,dt,Ft,Fn;if(R=G,q=Rr(),q===t&&(q=null),q!==t){if(Ce=G,r.charCodeAt(G)===35?(Ke=u,G++):(Ke=t,Xe===0&&Te(g)),Ke!==t){if(Re=[],ze=G,dt=G,Xe++,Ft=Us(),Xe--,Ft===t?dt=void 0:(G=dt,dt=t),dt!==t?(r.length>G?(Ft=r.charAt(G),G++):(Ft=t,Xe===0&&Te(f)),Ft!==t?(dt=[dt,Ft],ze=dt):(G=ze,ze=t)):(G=ze,ze=t),ze!==t)for(;ze!==t;)Re.push(ze),ze=G,dt=G,Xe++,Ft=Us(),Xe--,Ft===t?dt=void 0:(G=dt,dt=t),dt!==t?(r.length>G?(Ft=r.charAt(G),G++):(Ft=t,Xe===0&&Te(f)),Ft!==t?(dt=[dt,Ft],ze=dt):(G=ze,ze=t)):(G=ze,ze=t);else Re=t;Re!==t?(Ke=[Ke,Re],Ce=Ke):(G=Ce,Ce=t)}else G=Ce,Ce=t;if(Ce===t&&(Ce=null),Ce!==t){if(Ke=[],Re=Ks(),Re!==t)for(;Re!==t;)Ke.push(Re),Re=Ks();else Ke=t;Ke!==t?(yt=R,q=h(),R=q):(G=R,R=t)}else G=R,R=t}else G=R,R=t;if(R===t&&(R=G,q=ua(),q!==t?(Ce=Pl(),Ce!==t?(Ke=Rr(),Ke===t&&(Ke=null),Ke!==t?(r.charCodeAt(G)===58?(Re=p,G++):(Re=t,Xe===0&&Te(C)),Re!==t?(ze=Rr(),ze===t&&(ze=null),ze!==t?(dt=ca(),dt!==t?(yt=R,q=y(Ce,dt),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t),R===t&&(R=G,q=ua(),q!==t?(Ce=Ms(),Ce!==t?(Ke=Rr(),Ke===t&&(Ke=null),Ke!==t?(r.charCodeAt(G)===58?(Re=p,G++):(Re=t,Xe===0&&Te(C)),Re!==t?(ze=Rr(),ze===t&&(ze=null),ze!==t?(dt=ca(),dt!==t?(yt=R,q=y(Ce,dt),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t),R===t))){if(R=G,q=ua(),q!==t)if(Ce=Ms(),Ce!==t)if(Ke=Rr(),Ke!==t)if(Re=vE(),Re!==t){if(ze=[],dt=Ks(),dt!==t)for(;dt!==t;)ze.push(dt),dt=Ks();else ze=t;ze!==t?(yt=R,q=y(Ce,Re),R=q):(G=R,R=t)}else G=R,R=t;else G=R,R=t;else G=R,R=t;else G=R,R=t;if(R===t)if(R=G,q=ua(),q!==t)if(Ce=Ms(),Ce!==t){if(Ke=[],Re=G,ze=Rr(),ze===t&&(ze=null),ze!==t?(r.charCodeAt(G)===44?(dt=B,G++):(dt=t,Xe===0&&Te(v)),dt!==t?(Ft=Rr(),Ft===t&&(Ft=null),Ft!==t?(Fn=Ms(),Fn!==t?(yt=Re,ze=D(Ce,Fn),Re=ze):(G=Re,Re=t)):(G=Re,Re=t)):(G=Re,Re=t)):(G=Re,Re=t),Re!==t)for(;Re!==t;)Ke.push(Re),Re=G,ze=Rr(),ze===t&&(ze=null),ze!==t?(r.charCodeAt(G)===44?(dt=B,G++):(dt=t,Xe===0&&Te(v)),dt!==t?(Ft=Rr(),Ft===t&&(Ft=null),Ft!==t?(Fn=Ms(),Fn!==t?(yt=Re,ze=D(Ce,Fn),Re=ze):(G=Re,Re=t)):(G=Re,Re=t)):(G=Re,Re=t)):(G=Re,Re=t);else Ke=t;Ke!==t?(Re=Rr(),Re===t&&(Re=null),Re!==t?(r.charCodeAt(G)===58?(ze=p,G++):(ze=t,Xe===0&&Te(C)),ze!==t?(dt=Rr(),dt===t&&(dt=null),dt!==t?(Ft=ca(),Ft!==t?(yt=R,q=L(Ce,Ke,Ft),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)}else G=R,R=t;else G=R,R=t}return R}function ca(){var R,q,Ce,Ke,Re,ze,dt;if(R=G,q=G,Xe++,Ce=G,Ke=Us(),Ke!==t?(Re=rt(),Re!==t?(r.charCodeAt(G)===45?(ze=o,G++):(ze=t,Xe===0&&Te(a)),ze!==t?(dt=Rr(),dt!==t?(Ke=[Ke,Re,ze,dt],Ce=Ke):(G=Ce,Ce=t)):(G=Ce,Ce=t)):(G=Ce,Ce=t)):(G=Ce,Ce=t),Xe--,Ce!==t?(G=q,q=void 0):q=t,q!==t?(Ce=Ks(),Ce!==t?(Ke=Eo(),Ke!==t?(Re=xl(),Re!==t?(ze=fA(),ze!==t?(yt=R,q=H(Re),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t),R===t&&(R=G,q=Us(),q!==t?(Ce=Eo(),Ce!==t?(Ke=ag(),Ke!==t?(Re=fA(),Re!==t?(yt=R,q=H(Ke),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t),R===t))if(R=G,q=Dl(),q!==t){if(Ce=[],Ke=Ks(),Ke!==t)for(;Ke!==t;)Ce.push(Ke),Ke=Ks();else Ce=t;Ce!==t?(yt=R,q=j(q),R=q):(G=R,R=t)}else G=R,R=t;return R}function ua(){var R,q,Ce;for(Xe++,R=G,q=[],r.charCodeAt(G)===32?(Ce=V,G++):(Ce=t,Xe===0&&Te(W));Ce!==t;)q.push(Ce),r.charCodeAt(G)===32?(Ce=V,G++):(Ce=t,Xe===0&&Te(W));return q!==t?(yt=G,Ce=Z(q),Ce?Ce=void 0:Ce=t,Ce!==t?(q=[q,Ce],R=q):(G=R,R=t)):(G=R,R=t),Xe--,R===t&&(q=t,Xe===0&&Te($)),R}function rt(){var R,q,Ce;for(R=G,q=[],r.charCodeAt(G)===32?(Ce=V,G++):(Ce=t,Xe===0&&Te(W));Ce!==t;)q.push(Ce),r.charCodeAt(G)===32?(Ce=V,G++):(Ce=t,Xe===0&&Te(W));return q!==t?(yt=G,Ce=A(q),Ce?Ce=void 0:Ce=t,Ce!==t?(q=[q,Ce],R=q):(G=R,R=t)):(G=R,R=t),R}function Eo(){var R;return yt=G,R=ae(),R?R=void 0:R=t,R}function fA(){var R;return yt=G,R=ge(),R?R=void 0:R=t,R}function Pl(){var R;return R=kl(),R===t&&(R=yp()),R}function Ms(){var R,q,Ce;if(R=kl(),R===t){if(R=G,q=[],Ce=lg(),Ce!==t)for(;Ce!==t;)q.push(Ce),Ce=lg();else q=t;q!==t&&(yt=R,q=re()),R=q}return R}function Dl(){var R;return R=wp(),R===t&&(R=xE(),R===t&&(R=kl(),R===t&&(R=yp()))),R}function vE(){var R;return R=wp(),R===t&&(R=kl(),R===t&&(R=lg())),R}function yp(){var R,q,Ce,Ke,Re,ze;if(Xe++,R=G,F.test(r.charAt(G))?(q=r.charAt(G),G++):(q=t,Xe===0&&Te(ue)),q!==t){for(Ce=[],Ke=G,Re=Rr(),Re===t&&(Re=null),Re!==t?(he.test(r.charAt(G))?(ze=r.charAt(G),G++):(ze=t,Xe===0&&Te(ke)),ze!==t?(Re=[Re,ze],Ke=Re):(G=Ke,Ke=t)):(G=Ke,Ke=t);Ke!==t;)Ce.push(Ke),Ke=G,Re=Rr(),Re===t&&(Re=null),Re!==t?(he.test(r.charAt(G))?(ze=r.charAt(G),G++):(ze=t,Xe===0&&Te(ke)),ze!==t?(Re=[Re,ze],Ke=Re):(G=Ke,Ke=t)):(G=Ke,Ke=t);Ce!==t?(yt=R,q=Fe(),R=q):(G=R,R=t)}else G=R,R=t;return Xe--,R===t&&(q=t,Xe===0&&Te(O)),R}function lg(){var R,q,Ce,Ke,Re;if(R=G,r.substr(G,2)===Ne?(q=Ne,G+=2):(q=t,Xe===0&&Te(oe)),q===t&&(q=null),q!==t)if(le.test(r.charAt(G))?(Ce=r.charAt(G),G++):(Ce=t,Xe===0&&Te(we)),Ce!==t){for(Ke=[],fe.test(r.charAt(G))?(Re=r.charAt(G),G++):(Re=t,Xe===0&&Te(Ae));Re!==t;)Ke.push(Re),fe.test(r.charAt(G))?(Re=r.charAt(G),G++):(Re=t,Xe===0&&Te(Ae));Ke!==t?(yt=R,q=Fe(),R=q):(G=R,R=t)}else G=R,R=t;else G=R,R=t;return R}function wp(){var R,q;return R=G,r.substr(G,4)===qe?(q=qe,G+=4):(q=t,Xe===0&&Te(ne)),q!==t&&(yt=R,q=Y()),R=q,R}function xE(){var R,q;return R=G,r.substr(G,4)===pe?(q=pe,G+=4):(q=t,Xe===0&&Te(ie)),q!==t&&(yt=R,q=de()),R=q,R===t&&(R=G,r.substr(G,5)===tt?(q=tt,G+=5):(q=t,Xe===0&&Te(Pt)),q!==t&&(yt=R,q=It()),R=q),R}function kl(){var R,q,Ce,Ke;return Xe++,R=G,r.charCodeAt(G)===34?(q=ii,G++):(q=t,Xe===0&&Te(gi)),q!==t?(r.charCodeAt(G)===34?(Ce=ii,G++):(Ce=t,Xe===0&&Te(gi)),Ce!==t?(yt=R,q=hr(),R=q):(G=R,R=t)):(G=R,R=t),R===t&&(R=G,r.charCodeAt(G)===34?(q=ii,G++):(q=t,Xe===0&&Te(gi)),q!==t?(Ce=PE(),Ce!==t?(r.charCodeAt(G)===34?(Ke=ii,G++):(Ke=t,Xe===0&&Te(gi)),Ke!==t?(yt=R,q=fi(Ce),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)),Xe--,R===t&&(q=t,Xe===0&&Te(Or)),R}function PE(){var R,q,Ce;if(R=G,q=[],Ce=cg(),Ce!==t)for(;Ce!==t;)q.push(Ce),Ce=cg();else q=t;return q!==t&&(yt=R,q=ni(q)),R=q,R}function cg(){var R,q,Ce,Ke,Re,ze;return Ls.test(r.charAt(G))?(R=r.charAt(G),G++):(R=t,Xe===0&&Te(pr)),R===t&&(R=G,r.substr(G,2)===Ei?(q=Ei,G+=2):(q=t,Xe===0&&Te(_n)),q!==t&&(yt=R,q=oa()),R=q,R===t&&(R=G,r.substr(G,2)===aA?(q=aA,G+=2):(q=t,Xe===0&&Te(eg)),q!==t&&(yt=R,q=Zn()),R=q,R===t&&(R=G,r.substr(G,2)===AA?(q=AA,G+=2):(q=t,Xe===0&&Te(aa)),q!==t&&(yt=R,q=up()),R=q,R===t&&(R=G,r.substr(G,2)===lA?(q=lA,G+=2):(q=t,Xe===0&&Te(cA)),q!==t&&(yt=R,q=wr()),R=q,R===t&&(R=G,r.substr(G,2)===wl?(q=wl,G+=2):(q=t,Xe===0&&Te(tg)),q!==t&&(yt=R,q=po()),R=q,R===t&&(R=G,r.substr(G,2)===rg?(q=rg,G+=2):(q=t,Xe===0&&Te(gp)),q!==t&&(yt=R,q=fp()),R=q,R===t&&(R=G,r.substr(G,2)===vr?(q=vr,G+=2):(q=t,Xe===0&&Te(se)),q!==t&&(yt=R,q=Co()),R=q,R===t&&(R=G,r.substr(G,2)===Dn?(q=Dn,G+=2):(q=t,Xe===0&&Te(ig)),q!==t&&(yt=R,q=Qt()),R=q,R===t&&(R=G,r.substr(G,2)===Bl?(q=Bl,G+=2):(q=t,Xe===0&&Te(kn)),q!==t?(Ce=hA(),Ce!==t?(Ke=hA(),Ke!==t?(Re=hA(),Re!==t?(ze=hA(),ze!==t?(yt=R,q=$n(Ce,Ke,Re,ze),R=q):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)):(G=R,R=t)))))))))),R}function hA(){var R;return es.test(r.charAt(G))?(R=r.charAt(G),G++):(R=t,Xe===0&&Te(gt)),R}function Rr(){var R,q;if(Xe++,R=[],At.test(r.charAt(G))?(q=r.charAt(G),G++):(q=t,Xe===0&&Te(an)),q!==t)for(;q!==t;)R.push(q),At.test(r.charAt(G))?(q=r.charAt(G),G++):(q=t,Xe===0&&Te(an));else R=t;return Xe--,R===t&&(q=t,Xe===0&&Te(mo)),R}function DE(){var R,q;if(Xe++,R=[],Tt.test(r.charAt(G))?(q=r.charAt(G),G++):(q=t,Xe===0&&Te(ng)),q!==t)for(;q!==t;)R.push(q),Tt.test(r.charAt(G))?(q=r.charAt(G),G++):(q=t,Xe===0&&Te(ng));else R=t;return Xe--,R===t&&(q=t,Xe===0&&Te(S)),R}function Ks(){var R,q,Ce,Ke,Re,ze;if(R=G,q=Us(),q!==t){for(Ce=[],Ke=G,Re=Rr(),Re===t&&(Re=null),Re!==t?(ze=Us(),ze!==t?(Re=[Re,ze],Ke=Re):(G=Ke,Ke=t)):(G=Ke,Ke=t);Ke!==t;)Ce.push(Ke),Ke=G,Re=Rr(),Re===t&&(Re=null),Re!==t?(ze=Us(),ze!==t?(Re=[Re,ze],Ke=Re):(G=Ke,Ke=t)):(G=Ke,Ke=t);Ce!==t?(q=[q,Ce],R=q):(G=R,R=t)}else G=R,R=t;return R}function Us(){var R;return r.substr(G,2)===Ql?(R=Ql,G+=2):(R=t,Xe===0&&Te(hp)),R===t&&(r.charCodeAt(G)===10?(R=pp,G++):(R=t,Xe===0&&Te(dp)),R===t&&(r.charCodeAt(G)===13?(R=Cp,G++):(R=t,Xe===0&&Te(mp)))),R}let ug=2,pA=0;if(Aa=n(),Aa!==t&&G===r.length)return Aa;throw Aa!==t&&G{"use strict";var wpe=r=>{let e=!1,t=!1,i=!1;for(let n=0;n{if(!(typeof r=="string"||Array.isArray(r)))throw new TypeError("Expected the input to be `string | string[]`");e=Object.assign({pascalCase:!1},e);let t=n=>e.pascalCase?n.charAt(0).toUpperCase()+n.slice(1):n;return Array.isArray(r)?r=r.map(n=>n.trim()).filter(n=>n.length).join("-"):r=r.trim(),r.length===0?"":r.length===1?e.pascalCase?r.toUpperCase():r.toLowerCase():(r!==r.toLowerCase()&&(r=wpe(r)),r=r.replace(/^[_.\- ]+/,"").toLowerCase().replace(/[_.\- ]+(\w|$)/g,(n,s)=>s.toUpperCase()).replace(/\d+(\w|$)/g,n=>n.toUpperCase()),t(r))};KS.exports=T2;KS.exports.default=T2});var M2=w((VXe,Bpe)=>{Bpe.exports=[{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"Azure Pipelines",constant:"AZURE_PIPELINES",env:"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",pr:"SYSTEM_PULLREQUEST_PULLREQUESTID"},{name:"Appcircle",constant:"APPCIRCLE",env:"AC_APPCIRCLE"},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT",pr:"BITBUCKET_PR_ID"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Codefresh",constant:"CODEFRESH",env:"CF_BUILD_ID",pr:{any:["CF_PULL_REQUEST_NUMBER","CF_PULL_REQUEST_ID"]}},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"GitHub Actions",constant:"GITHUB_ACTIONS",env:"GITHUB_ACTIONS",pr:{GITHUB_EVENT_NAME:"pull_request"}},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI",pr:"CI_MERGE_REQUEST_ID"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"LayerCI",constant:"LAYERCI",env:"LAYERCI",pr:"LAYERCI_PULL_REQUEST"},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Netlify CI",constant:"NETLIFY",env:"NETLIFY",pr:{env:"PULL_REQUEST",ne:"false"}},{name:"Nevercode",constant:"NEVERCODE",env:"NEVERCODE",pr:{env:"NEVERCODE_PULL_REQUEST",ne:"false"}},{name:"Render",constant:"RENDER",env:"RENDER",pr:{IS_PULL_REQUEST:"true"}},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Screwdriver",constant:"SCREWDRIVER",env:"SCREWDRIVER",pr:{env:"SD_PULL_REQUEST",ne:"false"}},{name:"Shippable",constant:"SHIPPABLE",env:"SHIPPABLE",pr:{IS_PULL_REQUEST:"true"}},{name:"Solano CI",constant:"SOLANO",env:"TDDIUM",pr:"TDDIUM_PR_ID"},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}},{name:"Vercel",constant:"VERCEL",env:"NOW_BUILDER"},{name:"Visual Studio App Center",constant:"APPCENTER",env:"APPCENTER_BUILD_ID"}]});var Vl=w(On=>{"use strict";var U2=M2(),Qo=process.env;Object.defineProperty(On,"_vendors",{value:U2.map(function(r){return r.constant})});On.name=null;On.isPR=null;U2.forEach(function(r){let t=(Array.isArray(r.env)?r.env:[r.env]).every(function(i){return K2(i)});if(On[r.constant]=t,t)switch(On.name=r.name,typeof r.pr){case"string":On.isPR=!!Qo[r.pr];break;case"object":"env"in r.pr?On.isPR=r.pr.env in Qo&&Qo[r.pr.env]!==r.pr.ne:"any"in r.pr?On.isPR=r.pr.any.some(function(i){return!!Qo[i]}):On.isPR=K2(r.pr);break;default:On.isPR=null}});On.isCI=!!(Qo.CI||Qo.CONTINUOUS_INTEGRATION||Qo.BUILD_NUMBER||Qo.RUN_ID||On.name);function K2(r){return typeof r=="string"?!!Qo[r]:Object.keys(r).every(function(e){return Qo[e]===r[e]})}});var gn={};ut(gn,{KeyRelationship:()=>Xl,applyCascade:()=>zp,base64RegExp:()=>q2,colorStringAlphaRegExp:()=>j2,colorStringRegExp:()=>Y2,computeKey:()=>BA,getPrintable:()=>Vr,hasExactLength:()=>X2,hasForbiddenKeys:()=>tde,hasKeyRelationship:()=>JS,hasMaxLength:()=>Mpe,hasMinLength:()=>Ope,hasMutuallyExclusiveKeys:()=>rde,hasRequiredKeys:()=>ede,hasUniqueItems:()=>Kpe,isArray:()=>Ppe,isAtLeast:()=>Gpe,isAtMost:()=>Ype,isBase64:()=>Zpe,isBoolean:()=>Spe,isDate:()=>xpe,isDict:()=>kpe,isEnum:()=>Wi,isHexColor:()=>_pe,isISO8601:()=>Xpe,isInExclusiveRange:()=>qpe,isInInclusiveRange:()=>jpe,isInstanceOf:()=>Fpe,isInteger:()=>Jpe,isJSON:()=>$pe,isLiteral:()=>Qpe,isLowerCase:()=>Wpe,isNegative:()=>Upe,isNullable:()=>Tpe,isNumber:()=>vpe,isObject:()=>Rpe,isOneOf:()=>Npe,isOptional:()=>Lpe,isPositive:()=>Hpe,isString:()=>Wp,isTuple:()=>Dpe,isUUID4:()=>Vpe,isUnknown:()=>V2,isUpperCase:()=>zpe,iso8601RegExp:()=>qS,makeCoercionFn:()=>_l,makeSetter:()=>z2,makeTrait:()=>W2,makeValidator:()=>bt,matchesRegExp:()=>Vp,plural:()=>EI,pushError:()=>pt,simpleKeyRegExp:()=>G2,uuid4RegExp:()=>J2});function bt({test:r}){return W2(r)()}function Vr(r){return r===null?"null":r===void 0?"undefined":r===""?"an empty string":JSON.stringify(r)}function BA(r,e){var t,i,n;return typeof e=="number"?`${(t=r==null?void 0:r.p)!==null&&t!==void 0?t:"."}[${e}]`:G2.test(e)?`${(i=r==null?void 0:r.p)!==null&&i!==void 0?i:""}.${e}`:`${(n=r==null?void 0:r.p)!==null&&n!==void 0?n:"."}[${JSON.stringify(e)}]`}function _l(r,e){return t=>{let i=r[e];return r[e]=t,_l(r,e).bind(null,i)}}function z2(r,e){return t=>{r[e]=t}}function EI(r,e,t){return r===1?e:t}function pt({errors:r,p:e}={},t){return r==null||r.push(`${e!=null?e:"."}: ${t}`),!1}function Qpe(r){return bt({test:(e,t)=>e!==r?pt(t,`Expected a literal (got ${Vr(r)})`):!0})}function Wi(r){let e=Array.isArray(r)?r:Object.values(r),t=new Set(e);return bt({test:(i,n)=>t.has(i)?!0:pt(n,`Expected a valid enumeration value (got ${Vr(i)})`)})}var G2,Y2,j2,q2,J2,qS,W2,V2,Wp,bpe,Spe,vpe,xpe,Ppe,Dpe,kpe,Rpe,Fpe,Npe,zp,Lpe,Tpe,Ope,Mpe,X2,Kpe,Upe,Hpe,Gpe,Ype,jpe,qpe,Jpe,Vp,Wpe,zpe,Vpe,Xpe,_pe,Zpe,$pe,ede,tde,rde,Xl,ide,JS,ns=Yue(()=>{G2=/^[a-zA-Z_][a-zA-Z0-9_]*$/,Y2=/^#[0-9a-f]{6}$/i,j2=/^#[0-9a-f]{6}([0-9a-f]{2})?$/i,q2=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,J2=/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/i,qS=/^(?:[1-9]\d{3}(-?)(?:(?:0[1-9]|1[0-2])\1(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])\1(?:29|30)|(?:0[13578]|1[02])(?:\1)31|00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[0-5]))|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)(?:(-?)02(?:\2)29|-?366))T(?:[01]\d|2[0-3])(:?)[0-5]\d(?:\3[0-5]\d)?(?:Z|[+-][01]\d(?:\3[0-5]\d)?)$/,W2=r=>()=>r;V2=()=>bt({test:(r,e)=>!0});Wp=()=>bt({test:(r,e)=>typeof r!="string"?pt(e,`Expected a string (got ${Vr(r)})`):!0});bpe=new Map([["true",!0],["True",!0],["1",!0],[1,!0],["false",!1],["False",!1],["0",!1],[0,!1]]),Spe=()=>bt({test:(r,e)=>{var t;if(typeof r!="boolean"){if(typeof(e==null?void 0:e.coercions)<"u"){if(typeof(e==null?void 0:e.coercion)>"u")return pt(e,"Unbound coercion result");let i=bpe.get(r);if(typeof i<"u")return e.coercions.push([(t=e.p)!==null&&t!==void 0?t:".",e.coercion.bind(null,i)]),!0}return pt(e,`Expected a boolean (got ${Vr(r)})`)}return!0}}),vpe=()=>bt({test:(r,e)=>{var t;if(typeof r!="number"){if(typeof(e==null?void 0:e.coercions)<"u"){if(typeof(e==null?void 0:e.coercion)>"u")return pt(e,"Unbound coercion result");let i;if(typeof r=="string"){let n;try{n=JSON.parse(r)}catch{}if(typeof n=="number")if(JSON.stringify(n)===r)i=n;else return pt(e,`Received a number that can't be safely represented by the runtime (${r})`)}if(typeof i<"u")return e.coercions.push([(t=e.p)!==null&&t!==void 0?t:".",e.coercion.bind(null,i)]),!0}return pt(e,`Expected a number (got ${Vr(r)})`)}return!0}}),xpe=()=>bt({test:(r,e)=>{var t;if(!(r instanceof Date)){if(typeof(e==null?void 0:e.coercions)<"u"){if(typeof(e==null?void 0:e.coercion)>"u")return pt(e,"Unbound coercion result");let i;if(typeof r=="string"&&qS.test(r))i=new Date(r);else{let n;if(typeof r=="string"){let s;try{s=JSON.parse(r)}catch{}typeof s=="number"&&(n=s)}else typeof r=="number"&&(n=r);if(typeof n<"u")if(Number.isSafeInteger(n)||!Number.isSafeInteger(n*1e3))i=new Date(n*1e3);else return pt(e,`Received a timestamp that can't be safely represented by the runtime (${r})`)}if(typeof i<"u")return e.coercions.push([(t=e.p)!==null&&t!==void 0?t:".",e.coercion.bind(null,i)]),!0}return pt(e,`Expected a date (got ${Vr(r)})`)}return!0}}),Ppe=(r,{delimiter:e}={})=>bt({test:(t,i)=>{var n;if(typeof t=="string"&&typeof e<"u"&&typeof(i==null?void 0:i.coercions)<"u"){if(typeof(i==null?void 0:i.coercion)>"u")return pt(i,"Unbound coercion result");t=t.split(e),i.coercions.push([(n=i.p)!==null&&n!==void 0?n:".",i.coercion.bind(null,t)])}if(!Array.isArray(t))return pt(i,`Expected an array (got ${Vr(t)})`);let s=!0;for(let o=0,a=t.length;o{let t=X2(r.length);return bt({test:(i,n)=>{var s;if(typeof i=="string"&&typeof e<"u"&&typeof(n==null?void 0:n.coercions)<"u"){if(typeof(n==null?void 0:n.coercion)>"u")return pt(n,"Unbound coercion result");i=i.split(e),n.coercions.push([(s=n.p)!==null&&s!==void 0?s:".",n.coercion.bind(null,i)])}if(!Array.isArray(i))return pt(n,`Expected a tuple (got ${Vr(i)})`);let o=t(i,Object.assign({},n));for(let a=0,l=i.length;abt({test:(t,i)=>{if(typeof t!="object"||t===null)return pt(i,`Expected an object (got ${Vr(t)})`);let n=Object.keys(t),s=!0;for(let o=0,a=n.length;o{let t=Object.keys(r);return bt({test:(i,n)=>{if(typeof i!="object"||i===null)return pt(n,`Expected an object (got ${Vr(i)})`);let s=new Set([...t,...Object.keys(i)]),o={},a=!0;for(let l of s){if(l==="constructor"||l==="__proto__")a=pt(Object.assign(Object.assign({},n),{p:BA(n,l)}),"Unsafe property name");else{let c=Object.prototype.hasOwnProperty.call(r,l)?r[l]:void 0,u=Object.prototype.hasOwnProperty.call(i,l)?i[l]:void 0;typeof c<"u"?a=c(u,Object.assign(Object.assign({},n),{p:BA(n,l),coercion:_l(i,l)}))&&a:e===null?a=pt(Object.assign(Object.assign({},n),{p:BA(n,l)}),`Extraneous property (got ${Vr(u)})`):Object.defineProperty(o,l,{enumerable:!0,get:()=>u,set:z2(i,l)})}if(!a&&(n==null?void 0:n.errors)==null)break}return e!==null&&(a||(n==null?void 0:n.errors)!=null)&&(a=e(o,n)&&a),a}})},Fpe=r=>bt({test:(e,t)=>e instanceof r?!0:pt(t,`Expected an instance of ${r.name} (got ${Vr(e)})`)}),Npe=(r,{exclusive:e=!1}={})=>bt({test:(t,i)=>{var n,s,o;let a=[],l=typeof(i==null?void 0:i.errors)<"u"?[]:void 0;for(let c=0,u=r.length;c1?pt(i,`Expected to match exactly a single predicate (matched ${a.join(", ")})`):(o=i==null?void 0:i.errors)===null||o===void 0||o.push(...l),!1}}),zp=(r,e)=>bt({test:(t,i)=>{var n,s;let o={value:t},a=typeof(i==null?void 0:i.coercions)<"u"?_l(o,"value"):void 0,l=typeof(i==null?void 0:i.coercions)<"u"?[]:void 0;if(!r(t,Object.assign(Object.assign({},i),{coercion:a,coercions:l})))return!1;let c=[];if(typeof l<"u")for(let[,u]of l)c.push(u());try{if(typeof(i==null?void 0:i.coercions)<"u"){if(o.value!==t){if(typeof(i==null?void 0:i.coercion)>"u")return pt(i,"Unbound coercion result");i.coercions.push([(n=i.p)!==null&&n!==void 0?n:".",i.coercion.bind(null,o.value)])}(s=i==null?void 0:i.coercions)===null||s===void 0||s.push(...l)}return e.every(u=>u(o.value,i))}finally{for(let u of c)u()}}}),Lpe=r=>bt({test:(e,t)=>typeof e>"u"?!0:r(e,t)}),Tpe=r=>bt({test:(e,t)=>e===null?!0:r(e,t)}),Ope=r=>bt({test:(e,t)=>e.length>=r?!0:pt(t,`Expected to have a length of at least ${r} elements (got ${e.length})`)}),Mpe=r=>bt({test:(e,t)=>e.length<=r?!0:pt(t,`Expected to have a length of at most ${r} elements (got ${e.length})`)}),X2=r=>bt({test:(e,t)=>e.length!==r?pt(t,`Expected to have a length of exactly ${r} elements (got ${e.length})`):!0}),Kpe=({map:r}={})=>bt({test:(e,t)=>{let i=new Set,n=new Set;for(let s=0,o=e.length;sbt({test:(r,e)=>r<=0?!0:pt(e,`Expected to be negative (got ${r})`)}),Hpe=()=>bt({test:(r,e)=>r>=0?!0:pt(e,`Expected to be positive (got ${r})`)}),Gpe=r=>bt({test:(e,t)=>e>=r?!0:pt(t,`Expected to be at least ${r} (got ${e})`)}),Ype=r=>bt({test:(e,t)=>e<=r?!0:pt(t,`Expected to be at most ${r} (got ${e})`)}),jpe=(r,e)=>bt({test:(t,i)=>t>=r&&t<=e?!0:pt(i,`Expected to be in the [${r}; ${e}] range (got ${t})`)}),qpe=(r,e)=>bt({test:(t,i)=>t>=r&&tbt({test:(e,t)=>e!==Math.round(e)?pt(t,`Expected to be an integer (got ${e})`):Number.isSafeInteger(e)?!0:pt(t,`Expected to be a safe integer (got ${e})`)}),Vp=r=>bt({test:(e,t)=>r.test(e)?!0:pt(t,`Expected to match the pattern ${r.toString()} (got ${Vr(e)})`)}),Wpe=()=>bt({test:(r,e)=>r!==r.toLowerCase()?pt(e,`Expected to be all-lowercase (got ${r})`):!0}),zpe=()=>bt({test:(r,e)=>r!==r.toUpperCase()?pt(e,`Expected to be all-uppercase (got ${r})`):!0}),Vpe=()=>bt({test:(r,e)=>J2.test(r)?!0:pt(e,`Expected to be a valid UUID v4 (got ${Vr(r)})`)}),Xpe=()=>bt({test:(r,e)=>qS.test(r)?!1:pt(e,`Expected to be a valid ISO 8601 date string (got ${Vr(r)})`)}),_pe=({alpha:r=!1})=>bt({test:(e,t)=>(r?Y2.test(e):j2.test(e))?!0:pt(t,`Expected to be a valid hexadecimal color string (got ${Vr(e)})`)}),Zpe=()=>bt({test:(r,e)=>q2.test(r)?!0:pt(e,`Expected to be a valid base 64 string (got ${Vr(r)})`)}),$pe=(r=V2())=>bt({test:(e,t)=>{let i;try{i=JSON.parse(e)}catch{return pt(t,`Expected to be a valid JSON string (got ${Vr(e)})`)}return r(i,t)}}),ede=r=>{let e=new Set(r);return bt({test:(t,i)=>{let n=new Set(Object.keys(t)),s=[];for(let o of e)n.has(o)||s.push(o);return s.length>0?pt(i,`Missing required ${EI(s.length,"property","properties")} ${s.map(o=>`"${o}"`).join(", ")}`):!0}})},tde=r=>{let e=new Set(r);return bt({test:(t,i)=>{let n=new Set(Object.keys(t)),s=[];for(let o of e)n.has(o)&&s.push(o);return s.length>0?pt(i,`Forbidden ${EI(s.length,"property","properties")} ${s.map(o=>`"${o}"`).join(", ")}`):!0}})},rde=r=>{let e=new Set(r);return bt({test:(t,i)=>{let n=new Set(Object.keys(t)),s=[];for(let o of e)n.has(o)&&s.push(o);return s.length>1?pt(i,`Mutually exclusive properties ${s.map(o=>`"${o}"`).join(", ")}`):!0}})};(function(r){r.Forbids="Forbids",r.Requires="Requires"})(Xl||(Xl={}));ide={[Xl.Forbids]:{expect:!1,message:"forbids using"},[Xl.Requires]:{expect:!0,message:"requires using"}},JS=(r,e,t,{ignore:i=[]}={})=>{let n=new Set(i),s=new Set(t),o=ide[e];return bt({test:(a,l)=>{let c=new Set(Object.keys(a));if(!c.has(r)||n.has(a[r]))return!0;let u=[];for(let g of s)(c.has(g)&&!n.has(a[g]))!==o.expect&&u.push(g);return u.length>=1?pt(l,`Property "${r}" ${o.message} ${EI(u.length,"property","properties")} ${u.map(g=>`"${g}"`).join(", ")}`):!0}})}});var fH=w((V_e,gH)=>{"use strict";gH.exports=(r,...e)=>new Promise(t=>{t(r(...e))})});var Tg=w((X_e,ev)=>{"use strict";var Ide=fH(),hH=r=>{if(r<1)throw new TypeError("Expected `concurrency` to be a number from 1 and up");let e=[],t=0,i=()=>{t--,e.length>0&&e.shift()()},n=(a,l,...c)=>{t++;let u=Ide(a,...c);l(u),u.then(i,i)},s=(a,l,...c)=>{tnew Promise(c=>s(a,c,...l));return Object.defineProperties(o,{activeCount:{get:()=>t},pendingCount:{get:()=>e.length}}),o};ev.exports=hH;ev.exports.default=hH});var ed=w((Z_e,pH)=>{var yde="2.0.0",wde=Number.MAX_SAFE_INTEGER||9007199254740991,Bde=16;pH.exports={SEMVER_SPEC_VERSION:yde,MAX_LENGTH:256,MAX_SAFE_INTEGER:wde,MAX_SAFE_COMPONENT_LENGTH:Bde}});var td=w(($_e,dH)=>{var Qde=typeof process=="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...r)=>console.error("SEMVER",...r):()=>{};dH.exports=Qde});var Zl=w((bA,CH)=>{var{MAX_SAFE_COMPONENT_LENGTH:tv}=ed(),bde=td();bA=CH.exports={};var Sde=bA.re=[],$e=bA.src=[],et=bA.t={},vde=0,St=(r,e,t)=>{let i=vde++;bde(i,e),et[r]=i,$e[i]=e,Sde[i]=new RegExp(e,t?"g":void 0)};St("NUMERICIDENTIFIER","0|[1-9]\\d*");St("NUMERICIDENTIFIERLOOSE","[0-9]+");St("NONNUMERICIDENTIFIER","\\d*[a-zA-Z-][a-zA-Z0-9-]*");St("MAINVERSION",`(${$e[et.NUMERICIDENTIFIER]})\\.(${$e[et.NUMERICIDENTIFIER]})\\.(${$e[et.NUMERICIDENTIFIER]})`);St("MAINVERSIONLOOSE",`(${$e[et.NUMERICIDENTIFIERLOOSE]})\\.(${$e[et.NUMERICIDENTIFIERLOOSE]})\\.(${$e[et.NUMERICIDENTIFIERLOOSE]})`);St("PRERELEASEIDENTIFIER",`(?:${$e[et.NUMERICIDENTIFIER]}|${$e[et.NONNUMERICIDENTIFIER]})`);St("PRERELEASEIDENTIFIERLOOSE",`(?:${$e[et.NUMERICIDENTIFIERLOOSE]}|${$e[et.NONNUMERICIDENTIFIER]})`);St("PRERELEASE",`(?:-(${$e[et.PRERELEASEIDENTIFIER]}(?:\\.${$e[et.PRERELEASEIDENTIFIER]})*))`);St("PRERELEASELOOSE",`(?:-?(${$e[et.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${$e[et.PRERELEASEIDENTIFIERLOOSE]})*))`);St("BUILDIDENTIFIER","[0-9A-Za-z-]+");St("BUILD",`(?:\\+(${$e[et.BUILDIDENTIFIER]}(?:\\.${$e[et.BUILDIDENTIFIER]})*))`);St("FULLPLAIN",`v?${$e[et.MAINVERSION]}${$e[et.PRERELEASE]}?${$e[et.BUILD]}?`);St("FULL",`^${$e[et.FULLPLAIN]}$`);St("LOOSEPLAIN",`[v=\\s]*${$e[et.MAINVERSIONLOOSE]}${$e[et.PRERELEASELOOSE]}?${$e[et.BUILD]}?`);St("LOOSE",`^${$e[et.LOOSEPLAIN]}$`);St("GTLT","((?:<|>)?=?)");St("XRANGEIDENTIFIERLOOSE",`${$e[et.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);St("XRANGEIDENTIFIER",`${$e[et.NUMERICIDENTIFIER]}|x|X|\\*`);St("XRANGEPLAIN",`[v=\\s]*(${$e[et.XRANGEIDENTIFIER]})(?:\\.(${$e[et.XRANGEIDENTIFIER]})(?:\\.(${$e[et.XRANGEIDENTIFIER]})(?:${$e[et.PRERELEASE]})?${$e[et.BUILD]}?)?)?`);St("XRANGEPLAINLOOSE",`[v=\\s]*(${$e[et.XRANGEIDENTIFIERLOOSE]})(?:\\.(${$e[et.XRANGEIDENTIFIERLOOSE]})(?:\\.(${$e[et.XRANGEIDENTIFIERLOOSE]})(?:${$e[et.PRERELEASELOOSE]})?${$e[et.BUILD]}?)?)?`);St("XRANGE",`^${$e[et.GTLT]}\\s*${$e[et.XRANGEPLAIN]}$`);St("XRANGELOOSE",`^${$e[et.GTLT]}\\s*${$e[et.XRANGEPLAINLOOSE]}$`);St("COERCE",`(^|[^\\d])(\\d{1,${tv}})(?:\\.(\\d{1,${tv}}))?(?:\\.(\\d{1,${tv}}))?(?:$|[^\\d])`);St("COERCERTL",$e[et.COERCE],!0);St("LONETILDE","(?:~>?)");St("TILDETRIM",`(\\s*)${$e[et.LONETILDE]}\\s+`,!0);bA.tildeTrimReplace="$1~";St("TILDE",`^${$e[et.LONETILDE]}${$e[et.XRANGEPLAIN]}$`);St("TILDELOOSE",`^${$e[et.LONETILDE]}${$e[et.XRANGEPLAINLOOSE]}$`);St("LONECARET","(?:\\^)");St("CARETTRIM",`(\\s*)${$e[et.LONECARET]}\\s+`,!0);bA.caretTrimReplace="$1^";St("CARET",`^${$e[et.LONECARET]}${$e[et.XRANGEPLAIN]}$`);St("CARETLOOSE",`^${$e[et.LONECARET]}${$e[et.XRANGEPLAINLOOSE]}$`);St("COMPARATORLOOSE",`^${$e[et.GTLT]}\\s*(${$e[et.LOOSEPLAIN]})$|^$`);St("COMPARATOR",`^${$e[et.GTLT]}\\s*(${$e[et.FULLPLAIN]})$|^$`);St("COMPARATORTRIM",`(\\s*)${$e[et.GTLT]}\\s*(${$e[et.LOOSEPLAIN]}|${$e[et.XRANGEPLAIN]})`,!0);bA.comparatorTrimReplace="$1$2$3";St("HYPHENRANGE",`^\\s*(${$e[et.XRANGEPLAIN]})\\s+-\\s+(${$e[et.XRANGEPLAIN]})\\s*$`);St("HYPHENRANGELOOSE",`^\\s*(${$e[et.XRANGEPLAINLOOSE]})\\s+-\\s+(${$e[et.XRANGEPLAINLOOSE]})\\s*$`);St("STAR","(<|>)?=?\\s*\\*");St("GTE0","^\\s*>=\\s*0.0.0\\s*$");St("GTE0PRE","^\\s*>=\\s*0.0.0-0\\s*$")});var rd=w((eZe,mH)=>{var xde=["includePrerelease","loose","rtl"],Pde=r=>r?typeof r!="object"?{loose:!0}:xde.filter(e=>r[e]).reduce((e,t)=>(e[t]=!0,e),{}):{};mH.exports=Pde});var bI=w((tZe,yH)=>{var EH=/^[0-9]+$/,IH=(r,e)=>{let t=EH.test(r),i=EH.test(e);return t&&i&&(r=+r,e=+e),r===e?0:t&&!i?-1:i&&!t?1:rIH(e,r);yH.exports={compareIdentifiers:IH,rcompareIdentifiers:Dde}});var Li=w((rZe,bH)=>{var SI=td(),{MAX_LENGTH:wH,MAX_SAFE_INTEGER:vI}=ed(),{re:BH,t:QH}=Zl(),kde=rd(),{compareIdentifiers:id}=bI(),Un=class{constructor(e,t){if(t=kde(t),e instanceof Un){if(e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease)return e;e=e.version}else if(typeof e!="string")throw new TypeError(`Invalid Version: ${e}`);if(e.length>wH)throw new TypeError(`version is longer than ${wH} characters`);SI("SemVer",e,t),this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease;let i=e.trim().match(t.loose?BH[QH.LOOSE]:BH[QH.FULL]);if(!i)throw new TypeError(`Invalid Version: ${e}`);if(this.raw=e,this.major=+i[1],this.minor=+i[2],this.patch=+i[3],this.major>vI||this.major<0)throw new TypeError("Invalid major version");if(this.minor>vI||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>vI||this.patch<0)throw new TypeError("Invalid patch version");i[4]?this.prerelease=i[4].split(".").map(n=>{if(/^[0-9]+$/.test(n)){let s=+n;if(s>=0&&s=0;)typeof this.prerelease[i]=="number"&&(this.prerelease[i]++,i=-2);i===-1&&this.prerelease.push(0)}t&&(this.prerelease[0]===t?isNaN(this.prerelease[1])&&(this.prerelease=[t,0]):this.prerelease=[t,0]);break;default:throw new Error(`invalid increment argument: ${e}`)}return this.format(),this.raw=this.version,this}};bH.exports=Un});var $l=w((iZe,PH)=>{var{MAX_LENGTH:Rde}=ed(),{re:SH,t:vH}=Zl(),xH=Li(),Fde=rd(),Nde=(r,e)=>{if(e=Fde(e),r instanceof xH)return r;if(typeof r!="string"||r.length>Rde||!(e.loose?SH[vH.LOOSE]:SH[vH.FULL]).test(r))return null;try{return new xH(r,e)}catch{return null}};PH.exports=Nde});var kH=w((nZe,DH)=>{var Lde=$l(),Tde=(r,e)=>{let t=Lde(r,e);return t?t.version:null};DH.exports=Tde});var FH=w((sZe,RH)=>{var Ode=$l(),Mde=(r,e)=>{let t=Ode(r.trim().replace(/^[=v]+/,""),e);return t?t.version:null};RH.exports=Mde});var LH=w((oZe,NH)=>{var Kde=Li(),Ude=(r,e,t,i)=>{typeof t=="string"&&(i=t,t=void 0);try{return new Kde(r,t).inc(e,i).version}catch{return null}};NH.exports=Ude});var ss=w((aZe,OH)=>{var TH=Li(),Hde=(r,e,t)=>new TH(r,t).compare(new TH(e,t));OH.exports=Hde});var xI=w((AZe,MH)=>{var Gde=ss(),Yde=(r,e,t)=>Gde(r,e,t)===0;MH.exports=Yde});var HH=w((lZe,UH)=>{var KH=$l(),jde=xI(),qde=(r,e)=>{if(jde(r,e))return null;{let t=KH(r),i=KH(e),n=t.prerelease.length||i.prerelease.length,s=n?"pre":"",o=n?"prerelease":"";for(let a in t)if((a==="major"||a==="minor"||a==="patch")&&t[a]!==i[a])return s+a;return o}};UH.exports=qde});var YH=w((cZe,GH)=>{var Jde=Li(),Wde=(r,e)=>new Jde(r,e).major;GH.exports=Wde});var qH=w((uZe,jH)=>{var zde=Li(),Vde=(r,e)=>new zde(r,e).minor;jH.exports=Vde});var WH=w((gZe,JH)=>{var Xde=Li(),_de=(r,e)=>new Xde(r,e).patch;JH.exports=_de});var VH=w((fZe,zH)=>{var Zde=$l(),$de=(r,e)=>{let t=Zde(r,e);return t&&t.prerelease.length?t.prerelease:null};zH.exports=$de});var _H=w((hZe,XH)=>{var eCe=ss(),tCe=(r,e,t)=>eCe(e,r,t);XH.exports=tCe});var $H=w((pZe,ZH)=>{var rCe=ss(),iCe=(r,e)=>rCe(r,e,!0);ZH.exports=iCe});var PI=w((dZe,tG)=>{var eG=Li(),nCe=(r,e,t)=>{let i=new eG(r,t),n=new eG(e,t);return i.compare(n)||i.compareBuild(n)};tG.exports=nCe});var iG=w((CZe,rG)=>{var sCe=PI(),oCe=(r,e)=>r.sort((t,i)=>sCe(t,i,e));rG.exports=oCe});var sG=w((mZe,nG)=>{var aCe=PI(),ACe=(r,e)=>r.sort((t,i)=>aCe(i,t,e));nG.exports=ACe});var nd=w((EZe,oG)=>{var lCe=ss(),cCe=(r,e,t)=>lCe(r,e,t)>0;oG.exports=cCe});var DI=w((IZe,aG)=>{var uCe=ss(),gCe=(r,e,t)=>uCe(r,e,t)<0;aG.exports=gCe});var rv=w((yZe,AG)=>{var fCe=ss(),hCe=(r,e,t)=>fCe(r,e,t)!==0;AG.exports=hCe});var kI=w((wZe,lG)=>{var pCe=ss(),dCe=(r,e,t)=>pCe(r,e,t)>=0;lG.exports=dCe});var RI=w((BZe,cG)=>{var CCe=ss(),mCe=(r,e,t)=>CCe(r,e,t)<=0;cG.exports=mCe});var iv=w((QZe,uG)=>{var ECe=xI(),ICe=rv(),yCe=nd(),wCe=kI(),BCe=DI(),QCe=RI(),bCe=(r,e,t,i)=>{switch(e){case"===":return typeof r=="object"&&(r=r.version),typeof t=="object"&&(t=t.version),r===t;case"!==":return typeof r=="object"&&(r=r.version),typeof t=="object"&&(t=t.version),r!==t;case"":case"=":case"==":return ECe(r,t,i);case"!=":return ICe(r,t,i);case">":return yCe(r,t,i);case">=":return wCe(r,t,i);case"<":return BCe(r,t,i);case"<=":return QCe(r,t,i);default:throw new TypeError(`Invalid operator: ${e}`)}};uG.exports=bCe});var fG=w((bZe,gG)=>{var SCe=Li(),vCe=$l(),{re:FI,t:NI}=Zl(),xCe=(r,e)=>{if(r instanceof SCe)return r;if(typeof r=="number"&&(r=String(r)),typeof r!="string")return null;e=e||{};let t=null;if(!e.rtl)t=r.match(FI[NI.COERCE]);else{let i;for(;(i=FI[NI.COERCERTL].exec(r))&&(!t||t.index+t[0].length!==r.length);)(!t||i.index+i[0].length!==t.index+t[0].length)&&(t=i),FI[NI.COERCERTL].lastIndex=i.index+i[1].length+i[2].length;FI[NI.COERCERTL].lastIndex=-1}return t===null?null:vCe(`${t[2]}.${t[3]||"0"}.${t[4]||"0"}`,e)};gG.exports=xCe});var pG=w((SZe,hG)=>{"use strict";hG.exports=function(r){r.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next)yield e.value}}});var sd=w((vZe,dG)=>{"use strict";dG.exports=Ht;Ht.Node=ec;Ht.create=Ht;function Ht(r){var e=this;if(e instanceof Ht||(e=new Ht),e.tail=null,e.head=null,e.length=0,r&&typeof r.forEach=="function")r.forEach(function(n){e.push(n)});else if(arguments.length>0)for(var t=0,i=arguments.length;t1)t=e;else if(this.head)i=this.head.next,t=this.head.value;else throw new TypeError("Reduce of empty list with no initial value");for(var n=0;i!==null;n++)t=r(t,i.value,n),i=i.next;return t};Ht.prototype.reduceReverse=function(r,e){var t,i=this.tail;if(arguments.length>1)t=e;else if(this.tail)i=this.tail.prev,t=this.tail.value;else throw new TypeError("Reduce of empty list with no initial value");for(var n=this.length-1;i!==null;n--)t=r(t,i.value,n),i=i.prev;return t};Ht.prototype.toArray=function(){for(var r=new Array(this.length),e=0,t=this.head;t!==null;e++)r[e]=t.value,t=t.next;return r};Ht.prototype.toArrayReverse=function(){for(var r=new Array(this.length),e=0,t=this.tail;t!==null;e++)r[e]=t.value,t=t.prev;return r};Ht.prototype.slice=function(r,e){e=e||this.length,e<0&&(e+=this.length),r=r||0,r<0&&(r+=this.length);var t=new Ht;if(ethis.length&&(e=this.length);for(var i=0,n=this.head;n!==null&&ithis.length&&(e=this.length);for(var i=this.length,n=this.tail;n!==null&&i>e;i--)n=n.prev;for(;n!==null&&i>r;i--,n=n.prev)t.push(n.value);return t};Ht.prototype.splice=function(r,e,...t){r>this.length&&(r=this.length-1),r<0&&(r=this.length+r);for(var i=0,n=this.head;n!==null&&i{"use strict";var RCe=sd(),tc=Symbol("max"),Ia=Symbol("length"),Og=Symbol("lengthCalculator"),ad=Symbol("allowStale"),rc=Symbol("maxAge"),Ea=Symbol("dispose"),CG=Symbol("noDisposeOnSet"),di=Symbol("lruList"),Ws=Symbol("cache"),EG=Symbol("updateAgeOnGet"),nv=()=>1,ov=class{constructor(e){if(typeof e=="number"&&(e={max:e}),e||(e={}),e.max&&(typeof e.max!="number"||e.max<0))throw new TypeError("max must be a non-negative number");let t=this[tc]=e.max||1/0,i=e.length||nv;if(this[Og]=typeof i!="function"?nv:i,this[ad]=e.stale||!1,e.maxAge&&typeof e.maxAge!="number")throw new TypeError("maxAge must be a number");this[rc]=e.maxAge||0,this[Ea]=e.dispose,this[CG]=e.noDisposeOnSet||!1,this[EG]=e.updateAgeOnGet||!1,this.reset()}set max(e){if(typeof e!="number"||e<0)throw new TypeError("max must be a non-negative number");this[tc]=e||1/0,od(this)}get max(){return this[tc]}set allowStale(e){this[ad]=!!e}get allowStale(){return this[ad]}set maxAge(e){if(typeof e!="number")throw new TypeError("maxAge must be a non-negative number");this[rc]=e,od(this)}get maxAge(){return this[rc]}set lengthCalculator(e){typeof e!="function"&&(e=nv),e!==this[Og]&&(this[Og]=e,this[Ia]=0,this[di].forEach(t=>{t.length=this[Og](t.value,t.key),this[Ia]+=t.length})),od(this)}get lengthCalculator(){return this[Og]}get length(){return this[Ia]}get itemCount(){return this[di].length}rforEach(e,t){t=t||this;for(let i=this[di].tail;i!==null;){let n=i.prev;mG(this,e,i,t),i=n}}forEach(e,t){t=t||this;for(let i=this[di].head;i!==null;){let n=i.next;mG(this,e,i,t),i=n}}keys(){return this[di].toArray().map(e=>e.key)}values(){return this[di].toArray().map(e=>e.value)}reset(){this[Ea]&&this[di]&&this[di].length&&this[di].forEach(e=>this[Ea](e.key,e.value)),this[Ws]=new Map,this[di]=new RCe,this[Ia]=0}dump(){return this[di].map(e=>LI(this,e)?!1:{k:e.key,v:e.value,e:e.now+(e.maxAge||0)}).toArray().filter(e=>e)}dumpLru(){return this[di]}set(e,t,i){if(i=i||this[rc],i&&typeof i!="number")throw new TypeError("maxAge must be a number");let n=i?Date.now():0,s=this[Og](t,e);if(this[Ws].has(e)){if(s>this[tc])return Mg(this,this[Ws].get(e)),!1;let l=this[Ws].get(e).value;return this[Ea]&&(this[CG]||this[Ea](e,l.value)),l.now=n,l.maxAge=i,l.value=t,this[Ia]+=s-l.length,l.length=s,this.get(e),od(this),!0}let o=new av(e,t,s,n,i);return o.length>this[tc]?(this[Ea]&&this[Ea](e,t),!1):(this[Ia]+=o.length,this[di].unshift(o),this[Ws].set(e,this[di].head),od(this),!0)}has(e){if(!this[Ws].has(e))return!1;let t=this[Ws].get(e).value;return!LI(this,t)}get(e){return sv(this,e,!0)}peek(e){return sv(this,e,!1)}pop(){let e=this[di].tail;return e?(Mg(this,e),e.value):null}del(e){Mg(this,this[Ws].get(e))}load(e){this.reset();let t=Date.now();for(let i=e.length-1;i>=0;i--){let n=e[i],s=n.e||0;if(s===0)this.set(n.k,n.v);else{let o=s-t;o>0&&this.set(n.k,n.v,o)}}}prune(){this[Ws].forEach((e,t)=>sv(this,t,!1))}},sv=(r,e,t)=>{let i=r[Ws].get(e);if(i){let n=i.value;if(LI(r,n)){if(Mg(r,i),!r[ad])return}else t&&(r[EG]&&(i.value.now=Date.now()),r[di].unshiftNode(i));return n.value}},LI=(r,e)=>{if(!e||!e.maxAge&&!r[rc])return!1;let t=Date.now()-e.now;return e.maxAge?t>e.maxAge:r[rc]&&t>r[rc]},od=r=>{if(r[Ia]>r[tc])for(let e=r[di].tail;r[Ia]>r[tc]&&e!==null;){let t=e.prev;Mg(r,e),e=t}},Mg=(r,e)=>{if(e){let t=e.value;r[Ea]&&r[Ea](t.key,t.value),r[Ia]-=t.length,r[Ws].delete(t.key),r[di].removeNode(e)}},av=class{constructor(e,t,i,n,s){this.key=e,this.value=t,this.length=i,this.now=n,this.maxAge=s||0}},mG=(r,e,t,i)=>{let n=t.value;LI(r,n)&&(Mg(r,t),r[ad]||(n=void 0)),n&&e.call(i,n.value,n.key,r)};IG.exports=ov});var os=w((PZe,bG)=>{var ic=class{constructor(e,t){if(t=NCe(t),e instanceof ic)return e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease?e:new ic(e.raw,t);if(e instanceof Av)return this.raw=e.value,this.set=[[e]],this.format(),this;if(this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease,this.raw=e,this.set=e.split(/\s*\|\|\s*/).map(i=>this.parseRange(i.trim())).filter(i=>i.length),!this.set.length)throw new TypeError(`Invalid SemVer Range: ${e}`);if(this.set.length>1){let i=this.set[0];if(this.set=this.set.filter(n=>!BG(n[0])),this.set.length===0)this.set=[i];else if(this.set.length>1){for(let n of this.set)if(n.length===1&&KCe(n[0])){this.set=[n];break}}}this.format()}format(){return this.range=this.set.map(e=>e.join(" ").trim()).join("||").trim(),this.range}toString(){return this.range}parseRange(e){e=e.trim();let i=`parseRange:${Object.keys(this.options).join(",")}:${e}`,n=wG.get(i);if(n)return n;let s=this.options.loose,o=s?Ti[Bi.HYPHENRANGELOOSE]:Ti[Bi.HYPHENRANGE];e=e.replace(o,VCe(this.options.includePrerelease)),Gr("hyphen replace",e),e=e.replace(Ti[Bi.COMPARATORTRIM],TCe),Gr("comparator trim",e,Ti[Bi.COMPARATORTRIM]),e=e.replace(Ti[Bi.TILDETRIM],OCe),e=e.replace(Ti[Bi.CARETTRIM],MCe),e=e.split(/\s+/).join(" ");let a=s?Ti[Bi.COMPARATORLOOSE]:Ti[Bi.COMPARATOR],l=e.split(" ").map(f=>UCe(f,this.options)).join(" ").split(/\s+/).map(f=>zCe(f,this.options)).filter(this.options.loose?f=>!!f.match(a):()=>!0).map(f=>new Av(f,this.options)),c=l.length,u=new Map;for(let f of l){if(BG(f))return[f];u.set(f.value,f)}u.size>1&&u.has("")&&u.delete("");let g=[...u.values()];return wG.set(i,g),g}intersects(e,t){if(!(e instanceof ic))throw new TypeError("a Range is required");return this.set.some(i=>QG(i,t)&&e.set.some(n=>QG(n,t)&&i.every(s=>n.every(o=>s.intersects(o,t)))))}test(e){if(!e)return!1;if(typeof e=="string")try{e=new LCe(e,this.options)}catch{return!1}for(let t=0;tr.value==="<0.0.0-0",KCe=r=>r.value==="",QG=(r,e)=>{let t=!0,i=r.slice(),n=i.pop();for(;t&&i.length;)t=i.every(s=>n.intersects(s,e)),n=i.pop();return t},UCe=(r,e)=>(Gr("comp",r,e),r=YCe(r,e),Gr("caret",r),r=HCe(r,e),Gr("tildes",r),r=qCe(r,e),Gr("xrange",r),r=WCe(r,e),Gr("stars",r),r),Vi=r=>!r||r.toLowerCase()==="x"||r==="*",HCe=(r,e)=>r.trim().split(/\s+/).map(t=>GCe(t,e)).join(" "),GCe=(r,e)=>{let t=e.loose?Ti[Bi.TILDELOOSE]:Ti[Bi.TILDE];return r.replace(t,(i,n,s,o,a)=>{Gr("tilde",r,i,n,s,o,a);let l;return Vi(n)?l="":Vi(s)?l=`>=${n}.0.0 <${+n+1}.0.0-0`:Vi(o)?l=`>=${n}.${s}.0 <${n}.${+s+1}.0-0`:a?(Gr("replaceTilde pr",a),l=`>=${n}.${s}.${o}-${a} <${n}.${+s+1}.0-0`):l=`>=${n}.${s}.${o} <${n}.${+s+1}.0-0`,Gr("tilde return",l),l})},YCe=(r,e)=>r.trim().split(/\s+/).map(t=>jCe(t,e)).join(" "),jCe=(r,e)=>{Gr("caret",r,e);let t=e.loose?Ti[Bi.CARETLOOSE]:Ti[Bi.CARET],i=e.includePrerelease?"-0":"";return r.replace(t,(n,s,o,a,l)=>{Gr("caret",r,n,s,o,a,l);let c;return Vi(s)?c="":Vi(o)?c=`>=${s}.0.0${i} <${+s+1}.0.0-0`:Vi(a)?s==="0"?c=`>=${s}.${o}.0${i} <${s}.${+o+1}.0-0`:c=`>=${s}.${o}.0${i} <${+s+1}.0.0-0`:l?(Gr("replaceCaret pr",l),s==="0"?o==="0"?c=`>=${s}.${o}.${a}-${l} <${s}.${o}.${+a+1}-0`:c=`>=${s}.${o}.${a}-${l} <${s}.${+o+1}.0-0`:c=`>=${s}.${o}.${a}-${l} <${+s+1}.0.0-0`):(Gr("no pr"),s==="0"?o==="0"?c=`>=${s}.${o}.${a}${i} <${s}.${o}.${+a+1}-0`:c=`>=${s}.${o}.${a}${i} <${s}.${+o+1}.0-0`:c=`>=${s}.${o}.${a} <${+s+1}.0.0-0`),Gr("caret return",c),c})},qCe=(r,e)=>(Gr("replaceXRanges",r,e),r.split(/\s+/).map(t=>JCe(t,e)).join(" ")),JCe=(r,e)=>{r=r.trim();let t=e.loose?Ti[Bi.XRANGELOOSE]:Ti[Bi.XRANGE];return r.replace(t,(i,n,s,o,a,l)=>{Gr("xRange",r,i,n,s,o,a,l);let c=Vi(s),u=c||Vi(o),g=u||Vi(a),f=g;return n==="="&&f&&(n=""),l=e.includePrerelease?"-0":"",c?n===">"||n==="<"?i="<0.0.0-0":i="*":n&&f?(u&&(o=0),a=0,n===">"?(n=">=",u?(s=+s+1,o=0,a=0):(o=+o+1,a=0)):n==="<="&&(n="<",u?s=+s+1:o=+o+1),n==="<"&&(l="-0"),i=`${n+s}.${o}.${a}${l}`):u?i=`>=${s}.0.0${l} <${+s+1}.0.0-0`:g&&(i=`>=${s}.${o}.0${l} <${s}.${+o+1}.0-0`),Gr("xRange return",i),i})},WCe=(r,e)=>(Gr("replaceStars",r,e),r.trim().replace(Ti[Bi.STAR],"")),zCe=(r,e)=>(Gr("replaceGTE0",r,e),r.trim().replace(Ti[e.includePrerelease?Bi.GTE0PRE:Bi.GTE0],"")),VCe=r=>(e,t,i,n,s,o,a,l,c,u,g,f,h)=>(Vi(i)?t="":Vi(n)?t=`>=${i}.0.0${r?"-0":""}`:Vi(s)?t=`>=${i}.${n}.0${r?"-0":""}`:o?t=`>=${t}`:t=`>=${t}${r?"-0":""}`,Vi(c)?l="":Vi(u)?l=`<${+c+1}.0.0-0`:Vi(g)?l=`<${c}.${+u+1}.0-0`:f?l=`<=${c}.${u}.${g}-${f}`:r?l=`<${c}.${u}.${+g+1}-0`:l=`<=${l}`,`${t} ${l}`.trim()),XCe=(r,e,t)=>{for(let i=0;i0){let n=r[i].semver;if(n.major===e.major&&n.minor===e.minor&&n.patch===e.patch)return!0}return!1}return!0}});var Ad=w((DZe,DG)=>{var ld=Symbol("SemVer ANY"),Kg=class{static get ANY(){return ld}constructor(e,t){if(t=_Ce(t),e instanceof Kg){if(e.loose===!!t.loose)return e;e=e.value}cv("comparator",e,t),this.options=t,this.loose=!!t.loose,this.parse(e),this.semver===ld?this.value="":this.value=this.operator+this.semver.version,cv("comp",this)}parse(e){let t=this.options.loose?SG[vG.COMPARATORLOOSE]:SG[vG.COMPARATOR],i=e.match(t);if(!i)throw new TypeError(`Invalid comparator: ${e}`);this.operator=i[1]!==void 0?i[1]:"",this.operator==="="&&(this.operator=""),i[2]?this.semver=new xG(i[2],this.options.loose):this.semver=ld}toString(){return this.value}test(e){if(cv("Comparator.test",e,this.options.loose),this.semver===ld||e===ld)return!0;if(typeof e=="string")try{e=new xG(e,this.options)}catch{return!1}return lv(e,this.operator,this.semver,this.options)}intersects(e,t){if(!(e instanceof Kg))throw new TypeError("a Comparator is required");if((!t||typeof t!="object")&&(t={loose:!!t,includePrerelease:!1}),this.operator==="")return this.value===""?!0:new PG(e.value,t).test(this.value);if(e.operator==="")return e.value===""?!0:new PG(this.value,t).test(e.semver);let i=(this.operator===">="||this.operator===">")&&(e.operator===">="||e.operator===">"),n=(this.operator==="<="||this.operator==="<")&&(e.operator==="<="||e.operator==="<"),s=this.semver.version===e.semver.version,o=(this.operator===">="||this.operator==="<=")&&(e.operator===">="||e.operator==="<="),a=lv(this.semver,"<",e.semver,t)&&(this.operator===">="||this.operator===">")&&(e.operator==="<="||e.operator==="<"),l=lv(this.semver,">",e.semver,t)&&(this.operator==="<="||this.operator==="<")&&(e.operator===">="||e.operator===">");return i||n||s&&o||a||l}};DG.exports=Kg;var _Ce=rd(),{re:SG,t:vG}=Zl(),lv=iv(),cv=td(),xG=Li(),PG=os()});var cd=w((kZe,kG)=>{var ZCe=os(),$Ce=(r,e,t)=>{try{e=new ZCe(e,t)}catch{return!1}return e.test(r)};kG.exports=$Ce});var FG=w((RZe,RG)=>{var eme=os(),tme=(r,e)=>new eme(r,e).set.map(t=>t.map(i=>i.value).join(" ").trim().split(" "));RG.exports=tme});var LG=w((FZe,NG)=>{var rme=Li(),ime=os(),nme=(r,e,t)=>{let i=null,n=null,s=null;try{s=new ime(e,t)}catch{return null}return r.forEach(o=>{s.test(o)&&(!i||n.compare(o)===-1)&&(i=o,n=new rme(i,t))}),i};NG.exports=nme});var OG=w((NZe,TG)=>{var sme=Li(),ome=os(),ame=(r,e,t)=>{let i=null,n=null,s=null;try{s=new ome(e,t)}catch{return null}return r.forEach(o=>{s.test(o)&&(!i||n.compare(o)===1)&&(i=o,n=new sme(i,t))}),i};TG.exports=ame});var UG=w((LZe,KG)=>{var uv=Li(),Ame=os(),MG=nd(),lme=(r,e)=>{r=new Ame(r,e);let t=new uv("0.0.0");if(r.test(t)||(t=new uv("0.0.0-0"),r.test(t)))return t;t=null;for(let i=0;i{let a=new uv(o.semver.version);switch(o.operator){case">":a.prerelease.length===0?a.patch++:a.prerelease.push(0),a.raw=a.format();case"":case">=":(!s||MG(a,s))&&(s=a);break;case"<":case"<=":break;default:throw new Error(`Unexpected operation: ${o.operator}`)}}),s&&(!t||MG(t,s))&&(t=s)}return t&&r.test(t)?t:null};KG.exports=lme});var GG=w((TZe,HG)=>{var cme=os(),ume=(r,e)=>{try{return new cme(r,e).range||"*"}catch{return null}};HG.exports=ume});var TI=w((OZe,JG)=>{var gme=Li(),qG=Ad(),{ANY:fme}=qG,hme=os(),pme=cd(),YG=nd(),jG=DI(),dme=RI(),Cme=kI(),mme=(r,e,t,i)=>{r=new gme(r,i),e=new hme(e,i);let n,s,o,a,l;switch(t){case">":n=YG,s=dme,o=jG,a=">",l=">=";break;case"<":n=jG,s=Cme,o=YG,a="<",l="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(pme(r,e,i))return!1;for(let c=0;c{h.semver===fme&&(h=new qG(">=0.0.0")),g=g||h,f=f||h,n(h.semver,g.semver,i)?g=h:o(h.semver,f.semver,i)&&(f=h)}),g.operator===a||g.operator===l||(!f.operator||f.operator===a)&&s(r,f.semver))return!1;if(f.operator===l&&o(r,f.semver))return!1}return!0};JG.exports=mme});var zG=w((MZe,WG)=>{var Eme=TI(),Ime=(r,e,t)=>Eme(r,e,">",t);WG.exports=Ime});var XG=w((KZe,VG)=>{var yme=TI(),wme=(r,e,t)=>yme(r,e,"<",t);VG.exports=wme});var $G=w((UZe,ZG)=>{var _G=os(),Bme=(r,e,t)=>(r=new _G(r,t),e=new _G(e,t),r.intersects(e));ZG.exports=Bme});var tY=w((HZe,eY)=>{var Qme=cd(),bme=ss();eY.exports=(r,e,t)=>{let i=[],n=null,s=null,o=r.sort((u,g)=>bme(u,g,t));for(let u of o)Qme(u,e,t)?(s=u,n||(n=u)):(s&&i.push([n,s]),s=null,n=null);n&&i.push([n,null]);let a=[];for(let[u,g]of i)u===g?a.push(u):!g&&u===o[0]?a.push("*"):g?u===o[0]?a.push(`<=${g}`):a.push(`${u} - ${g}`):a.push(`>=${u}`);let l=a.join(" || "),c=typeof e.raw=="string"?e.raw:String(e);return l.length{var rY=os(),OI=Ad(),{ANY:gv}=OI,ud=cd(),fv=ss(),Sme=(r,e,t={})=>{if(r===e)return!0;r=new rY(r,t),e=new rY(e,t);let i=!1;e:for(let n of r.set){for(let s of e.set){let o=vme(n,s,t);if(i=i||o!==null,o)continue e}if(i)return!1}return!0},vme=(r,e,t)=>{if(r===e)return!0;if(r.length===1&&r[0].semver===gv){if(e.length===1&&e[0].semver===gv)return!0;t.includePrerelease?r=[new OI(">=0.0.0-0")]:r=[new OI(">=0.0.0")]}if(e.length===1&&e[0].semver===gv){if(t.includePrerelease)return!0;e=[new OI(">=0.0.0")]}let i=new Set,n,s;for(let h of r)h.operator===">"||h.operator===">="?n=iY(n,h,t):h.operator==="<"||h.operator==="<="?s=nY(s,h,t):i.add(h.semver);if(i.size>1)return null;let o;if(n&&s){if(o=fv(n.semver,s.semver,t),o>0)return null;if(o===0&&(n.operator!==">="||s.operator!=="<="))return null}for(let h of i){if(n&&!ud(h,String(n),t)||s&&!ud(h,String(s),t))return null;for(let p of e)if(!ud(h,String(p),t))return!1;return!0}let a,l,c,u,g=s&&!t.includePrerelease&&s.semver.prerelease.length?s.semver:!1,f=n&&!t.includePrerelease&&n.semver.prerelease.length?n.semver:!1;g&&g.prerelease.length===1&&s.operator==="<"&&g.prerelease[0]===0&&(g=!1);for(let h of e){if(u=u||h.operator===">"||h.operator===">=",c=c||h.operator==="<"||h.operator==="<=",n){if(f&&h.semver.prerelease&&h.semver.prerelease.length&&h.semver.major===f.major&&h.semver.minor===f.minor&&h.semver.patch===f.patch&&(f=!1),h.operator===">"||h.operator===">="){if(a=iY(n,h,t),a===h&&a!==n)return!1}else if(n.operator===">="&&!ud(n.semver,String(h),t))return!1}if(s){if(g&&h.semver.prerelease&&h.semver.prerelease.length&&h.semver.major===g.major&&h.semver.minor===g.minor&&h.semver.patch===g.patch&&(g=!1),h.operator==="<"||h.operator==="<="){if(l=nY(s,h,t),l===h&&l!==s)return!1}else if(s.operator==="<="&&!ud(s.semver,String(h),t))return!1}if(!h.operator&&(s||n)&&o!==0)return!1}return!(n&&c&&!s&&o!==0||s&&u&&!n&&o!==0||f||g)},iY=(r,e,t)=>{if(!r)return e;let i=fv(r.semver,e.semver,t);return i>0?r:i<0||e.operator===">"&&r.operator===">="?e:r},nY=(r,e,t)=>{if(!r)return e;let i=fv(r.semver,e.semver,t);return i<0?r:i>0||e.operator==="<"&&r.operator==="<="?e:r};sY.exports=Sme});var Xr=w((YZe,aY)=>{var hv=Zl();aY.exports={re:hv.re,src:hv.src,tokens:hv.t,SEMVER_SPEC_VERSION:ed().SEMVER_SPEC_VERSION,SemVer:Li(),compareIdentifiers:bI().compareIdentifiers,rcompareIdentifiers:bI().rcompareIdentifiers,parse:$l(),valid:kH(),clean:FH(),inc:LH(),diff:HH(),major:YH(),minor:qH(),patch:WH(),prerelease:VH(),compare:ss(),rcompare:_H(),compareLoose:$H(),compareBuild:PI(),sort:iG(),rsort:sG(),gt:nd(),lt:DI(),eq:xI(),neq:rv(),gte:kI(),lte:RI(),cmp:iv(),coerce:fG(),Comparator:Ad(),Range:os(),satisfies:cd(),toComparators:FG(),maxSatisfying:LG(),minSatisfying:OG(),minVersion:UG(),validRange:GG(),outside:TI(),gtr:zG(),ltr:XG(),intersects:$G(),simplifyRange:tY(),subset:oY()}});var pv=w(MI=>{"use strict";Object.defineProperty(MI,"__esModule",{value:!0});MI.VERSION=void 0;MI.VERSION="9.1.0"});var Gt=w((exports,module)=>{"use strict";var __spreadArray=exports&&exports.__spreadArray||function(r,e,t){if(t||arguments.length===2)for(var i=0,n=e.length,s;i{(function(r,e){typeof define=="function"&&define.amd?define([],e):typeof KI=="object"&&KI.exports?KI.exports=e():r.regexpToAst=e()})(typeof self<"u"?self:AY,function(){function r(){}r.prototype.saveState=function(){return{idx:this.idx,input:this.input,groupIdx:this.groupIdx}},r.prototype.restoreState=function(p){this.idx=p.idx,this.input=p.input,this.groupIdx=p.groupIdx},r.prototype.pattern=function(p){this.idx=0,this.input=p,this.groupIdx=0,this.consumeChar("/");var C=this.disjunction();this.consumeChar("/");for(var y={type:"Flags",loc:{begin:this.idx,end:p.length},global:!1,ignoreCase:!1,multiLine:!1,unicode:!1,sticky:!1};this.isRegExpFlag();)switch(this.popChar()){case"g":o(y,"global");break;case"i":o(y,"ignoreCase");break;case"m":o(y,"multiLine");break;case"u":o(y,"unicode");break;case"y":o(y,"sticky");break}if(this.idx!==this.input.length)throw Error("Redundant input: "+this.input.substring(this.idx));return{type:"Pattern",flags:y,value:C,loc:this.loc(0)}},r.prototype.disjunction=function(){var p=[],C=this.idx;for(p.push(this.alternative());this.peekChar()==="|";)this.consumeChar("|"),p.push(this.alternative());return{type:"Disjunction",value:p,loc:this.loc(C)}},r.prototype.alternative=function(){for(var p=[],C=this.idx;this.isTerm();)p.push(this.term());return{type:"Alternative",value:p,loc:this.loc(C)}},r.prototype.term=function(){return this.isAssertion()?this.assertion():this.atom()},r.prototype.assertion=function(){var p=this.idx;switch(this.popChar()){case"^":return{type:"StartAnchor",loc:this.loc(p)};case"$":return{type:"EndAnchor",loc:this.loc(p)};case"\\":switch(this.popChar()){case"b":return{type:"WordBoundary",loc:this.loc(p)};case"B":return{type:"NonWordBoundary",loc:this.loc(p)}}throw Error("Invalid Assertion Escape");case"(":this.consumeChar("?");var C;switch(this.popChar()){case"=":C="Lookahead";break;case"!":C="NegativeLookahead";break}a(C);var y=this.disjunction();return this.consumeChar(")"),{type:C,value:y,loc:this.loc(p)}}l()},r.prototype.quantifier=function(p){var C,y=this.idx;switch(this.popChar()){case"*":C={atLeast:0,atMost:1/0};break;case"+":C={atLeast:1,atMost:1/0};break;case"?":C={atLeast:0,atMost:1};break;case"{":var B=this.integerIncludingZero();switch(this.popChar()){case"}":C={atLeast:B,atMost:B};break;case",":var v;this.isDigit()?(v=this.integerIncludingZero(),C={atLeast:B,atMost:v}):C={atLeast:B,atMost:1/0},this.consumeChar("}");break}if(p===!0&&C===void 0)return;a(C);break}if(!(p===!0&&C===void 0))return a(C),this.peekChar(0)==="?"?(this.consumeChar("?"),C.greedy=!1):C.greedy=!0,C.type="Quantifier",C.loc=this.loc(y),C},r.prototype.atom=function(){var p,C=this.idx;switch(this.peekChar()){case".":p=this.dotAll();break;case"\\":p=this.atomEscape();break;case"[":p=this.characterClass();break;case"(":p=this.group();break}return p===void 0&&this.isPatternCharacter()&&(p=this.patternCharacter()),a(p),p.loc=this.loc(C),this.isQuantifier()&&(p.quantifier=this.quantifier()),p},r.prototype.dotAll=function(){return this.consumeChar("."),{type:"Set",complement:!0,value:[n(` +`),n("\r"),n("\u2028"),n("\u2029")]}},r.prototype.atomEscape=function(){switch(this.consumeChar("\\"),this.peekChar()){case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":return this.decimalEscapeAtom();case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}},r.prototype.decimalEscapeAtom=function(){var p=this.positiveInteger();return{type:"GroupBackReference",value:p}},r.prototype.characterClassEscape=function(){var p,C=!1;switch(this.popChar()){case"d":p=u;break;case"D":p=u,C=!0;break;case"s":p=f;break;case"S":p=f,C=!0;break;case"w":p=g;break;case"W":p=g,C=!0;break}return a(p),{type:"Set",value:p,complement:C}},r.prototype.controlEscapeAtom=function(){var p;switch(this.popChar()){case"f":p=n("\f");break;case"n":p=n(` +`);break;case"r":p=n("\r");break;case"t":p=n(" ");break;case"v":p=n("\v");break}return a(p),{type:"Character",value:p}},r.prototype.controlLetterEscapeAtom=function(){this.consumeChar("c");var p=this.popChar();if(/[a-zA-Z]/.test(p)===!1)throw Error("Invalid ");var C=p.toUpperCase().charCodeAt(0)-64;return{type:"Character",value:C}},r.prototype.nulCharacterAtom=function(){return this.consumeChar("0"),{type:"Character",value:n("\0")}},r.prototype.hexEscapeSequenceAtom=function(){return this.consumeChar("x"),this.parseHexDigits(2)},r.prototype.regExpUnicodeEscapeSequenceAtom=function(){return this.consumeChar("u"),this.parseHexDigits(4)},r.prototype.identityEscapeAtom=function(){var p=this.popChar();return{type:"Character",value:n(p)}},r.prototype.classPatternCharacterAtom=function(){switch(this.peekChar()){case` +`:case"\r":case"\u2028":case"\u2029":case"\\":case"]":throw Error("TBD");default:var p=this.popChar();return{type:"Character",value:n(p)}}},r.prototype.characterClass=function(){var p=[],C=!1;for(this.consumeChar("["),this.peekChar(0)==="^"&&(this.consumeChar("^"),C=!0);this.isClassAtom();){var y=this.classAtom(),B=y.type==="Character";if(B&&this.isRangeDash()){this.consumeChar("-");var v=this.classAtom(),D=v.type==="Character";if(D){if(v.value=this.input.length)throw Error("Unexpected end of input");this.idx++},r.prototype.loc=function(p){return{begin:p,end:this.idx}};var e=/[0-9a-fA-F]/,t=/[0-9]/,i=/[1-9]/;function n(p){return p.charCodeAt(0)}function s(p,C){p.length!==void 0?p.forEach(function(y){C.push(y)}):C.push(p)}function o(p,C){if(p[C]===!0)throw"duplicate flag "+C;p[C]=!0}function a(p){if(p===void 0)throw Error("Internal Error - Should never get here!")}function l(){throw Error("Internal Error - Should never get here!")}var c,u=[];for(c=n("0");c<=n("9");c++)u.push(c);var g=[n("_")].concat(u);for(c=n("a");c<=n("z");c++)g.push(c);for(c=n("A");c<=n("Z");c++)g.push(c);var f=[n(" "),n("\f"),n(` +`),n("\r"),n(" "),n("\v"),n(" "),n("\xA0"),n("\u1680"),n("\u2000"),n("\u2001"),n("\u2002"),n("\u2003"),n("\u2004"),n("\u2005"),n("\u2006"),n("\u2007"),n("\u2008"),n("\u2009"),n("\u200A"),n("\u2028"),n("\u2029"),n("\u202F"),n("\u205F"),n("\u3000"),n("\uFEFF")];function h(){}return h.prototype.visitChildren=function(p){for(var C in p){var y=p[C];p.hasOwnProperty(C)&&(y.type!==void 0?this.visit(y):Array.isArray(y)&&y.forEach(function(B){this.visit(B)},this))}},h.prototype.visit=function(p){switch(p.type){case"Pattern":this.visitPattern(p);break;case"Flags":this.visitFlags(p);break;case"Disjunction":this.visitDisjunction(p);break;case"Alternative":this.visitAlternative(p);break;case"StartAnchor":this.visitStartAnchor(p);break;case"EndAnchor":this.visitEndAnchor(p);break;case"WordBoundary":this.visitWordBoundary(p);break;case"NonWordBoundary":this.visitNonWordBoundary(p);break;case"Lookahead":this.visitLookahead(p);break;case"NegativeLookahead":this.visitNegativeLookahead(p);break;case"Character":this.visitCharacter(p);break;case"Set":this.visitSet(p);break;case"Group":this.visitGroup(p);break;case"GroupBackReference":this.visitGroupBackReference(p);break;case"Quantifier":this.visitQuantifier(p);break}this.visitChildren(p)},h.prototype.visitPattern=function(p){},h.prototype.visitFlags=function(p){},h.prototype.visitDisjunction=function(p){},h.prototype.visitAlternative=function(p){},h.prototype.visitStartAnchor=function(p){},h.prototype.visitEndAnchor=function(p){},h.prototype.visitWordBoundary=function(p){},h.prototype.visitNonWordBoundary=function(p){},h.prototype.visitLookahead=function(p){},h.prototype.visitNegativeLookahead=function(p){},h.prototype.visitCharacter=function(p){},h.prototype.visitSet=function(p){},h.prototype.visitGroup=function(p){},h.prototype.visitGroupBackReference=function(p){},h.prototype.visitQuantifier=function(p){},{RegExpParser:r,BaseRegExpVisitor:h,VERSION:"0.5.0"}})});var GI=w(Ug=>{"use strict";Object.defineProperty(Ug,"__esModule",{value:!0});Ug.clearRegExpParserCache=Ug.getRegExpAst=void 0;var xme=UI(),HI={},Pme=new xme.RegExpParser;function Dme(r){var e=r.toString();if(HI.hasOwnProperty(e))return HI[e];var t=Pme.pattern(e);return HI[e]=t,t}Ug.getRegExpAst=Dme;function kme(){HI={}}Ug.clearRegExpParserCache=kme});var fY=w(pn=>{"use strict";var Rme=pn&&pn.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(pn,"__esModule",{value:!0});pn.canMatchCharCode=pn.firstCharOptimizedIndices=pn.getOptimizedStartCodesIndices=pn.failedOptimizationPrefixMsg=void 0;var cY=UI(),as=Gt(),uY=GI(),ya=Cv(),gY="Complement Sets are not supported for first char optimization";pn.failedOptimizationPrefixMsg=`Unable to use "first char" lexer optimizations: +`;function Fme(r,e){e===void 0&&(e=!1);try{var t=(0,uY.getRegExpAst)(r),i=jI(t.value,{},t.flags.ignoreCase);return i}catch(s){if(s.message===gY)e&&(0,as.PRINT_WARNING)(""+pn.failedOptimizationPrefixMsg+(" Unable to optimize: < "+r.toString()+` > +`)+` Complement Sets cannot be automatically optimized. + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);else{var n="";e&&(n=` + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.`),(0,as.PRINT_ERROR)(pn.failedOptimizationPrefixMsg+` +`+(" Failed parsing: < "+r.toString()+` > +`)+(" Using the regexp-to-ast library version: "+cY.VERSION+` +`)+" Please open an issue at: https://github.com/bd82/regexp-to-ast/issues"+n)}}return[]}pn.getOptimizedStartCodesIndices=Fme;function jI(r,e,t){switch(r.type){case"Disjunction":for(var i=0;i=ya.minOptimizationVal)for(var f=u.from>=ya.minOptimizationVal?u.from:ya.minOptimizationVal,h=u.to,p=(0,ya.charCodeToOptimizedIndex)(f),C=(0,ya.charCodeToOptimizedIndex)(h),y=p;y<=C;y++)e[y]=y}}});break;case"Group":jI(o.value,e,t);break;default:throw Error("Non Exhaustive Match")}var a=o.quantifier!==void 0&&o.quantifier.atLeast===0;if(o.type==="Group"&&dv(o)===!1||o.type!=="Group"&&a===!1)break}break;default:throw Error("non exhaustive match!")}return(0,as.values)(e)}pn.firstCharOptimizedIndices=jI;function YI(r,e,t){var i=(0,ya.charCodeToOptimizedIndex)(r);e[i]=i,t===!0&&Nme(r,e)}function Nme(r,e){var t=String.fromCharCode(r),i=t.toUpperCase();if(i!==t){var n=(0,ya.charCodeToOptimizedIndex)(i.charCodeAt(0));e[n]=n}else{var s=t.toLowerCase();if(s!==t){var n=(0,ya.charCodeToOptimizedIndex)(s.charCodeAt(0));e[n]=n}}}function lY(r,e){return(0,as.find)(r.value,function(t){if(typeof t=="number")return(0,as.contains)(e,t);var i=t;return(0,as.find)(e,function(n){return i.from<=n&&n<=i.to})!==void 0})}function dv(r){return r.quantifier&&r.quantifier.atLeast===0?!0:r.value?(0,as.isArray)(r.value)?(0,as.every)(r.value,dv):dv(r.value):!1}var Lme=function(r){Rme(e,r);function e(t){var i=r.call(this)||this;return i.targetCharCodes=t,i.found=!1,i}return e.prototype.visitChildren=function(t){if(this.found!==!0){switch(t.type){case"Lookahead":this.visitLookahead(t);return;case"NegativeLookahead":this.visitNegativeLookahead(t);return}r.prototype.visitChildren.call(this,t)}},e.prototype.visitCharacter=function(t){(0,as.contains)(this.targetCharCodes,t.value)&&(this.found=!0)},e.prototype.visitSet=function(t){t.complement?lY(t,this.targetCharCodes)===void 0&&(this.found=!0):lY(t,this.targetCharCodes)!==void 0&&(this.found=!0)},e}(cY.BaseRegExpVisitor);function Tme(r,e){if(e instanceof RegExp){var t=(0,uY.getRegExpAst)(e),i=new Lme(r);return i.visit(t),i.found}else return(0,as.find)(e,function(n){return(0,as.contains)(r,n.charCodeAt(0))})!==void 0}pn.canMatchCharCode=Tme});var Cv=w(Ve=>{"use strict";var hY=Ve&&Ve.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(Ve,"__esModule",{value:!0});Ve.charCodeToOptimizedIndex=Ve.minOptimizationVal=Ve.buildLineBreakIssueMessage=Ve.LineTerminatorOptimizedTester=Ve.isShortPattern=Ve.isCustomPattern=Ve.cloneEmptyGroups=Ve.performWarningRuntimeChecks=Ve.performRuntimeChecks=Ve.addStickyFlag=Ve.addStartOfInput=Ve.findUnreachablePatterns=Ve.findModesThatDoNotExist=Ve.findInvalidGroupType=Ve.findDuplicatePatterns=Ve.findUnsupportedFlags=Ve.findStartOfInputAnchor=Ve.findEmptyMatchRegExps=Ve.findEndOfInputAnchor=Ve.findInvalidPatterns=Ve.findMissingPatterns=Ve.validatePatterns=Ve.analyzeTokenTypes=Ve.enableSticky=Ve.disableSticky=Ve.SUPPORT_STICKY=Ve.MODES=Ve.DEFAULT_MODE=void 0;var pY=UI(),ir=gd(),xe=Gt(),Hg=fY(),dY=GI(),So="PATTERN";Ve.DEFAULT_MODE="defaultMode";Ve.MODES="modes";Ve.SUPPORT_STICKY=typeof new RegExp("(?:)").sticky=="boolean";function Ome(){Ve.SUPPORT_STICKY=!1}Ve.disableSticky=Ome;function Mme(){Ve.SUPPORT_STICKY=!0}Ve.enableSticky=Mme;function Kme(r,e){e=(0,xe.defaults)(e,{useSticky:Ve.SUPPORT_STICKY,debug:!1,safeMode:!1,positionTracking:"full",lineTerminatorCharacters:["\r",` +`],tracer:function(v,D){return D()}});var t=e.tracer;t("initCharCodeToOptimizedIndexMap",function(){Vme()});var i;t("Reject Lexer.NA",function(){i=(0,xe.reject)(r,function(v){return v[So]===ir.Lexer.NA})});var n=!1,s;t("Transform Patterns",function(){n=!1,s=(0,xe.map)(i,function(v){var D=v[So];if((0,xe.isRegExp)(D)){var L=D.source;return L.length===1&&L!=="^"&&L!=="$"&&L!=="."&&!D.ignoreCase?L:L.length===2&&L[0]==="\\"&&!(0,xe.contains)(["d","D","s","S","t","r","n","t","0","c","b","B","f","v","w","W"],L[1])?L[1]:e.useSticky?Iv(D):Ev(D)}else{if((0,xe.isFunction)(D))return n=!0,{exec:D};if((0,xe.has)(D,"exec"))return n=!0,D;if(typeof D=="string"){if(D.length===1)return D;var H=D.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&"),j=new RegExp(H);return e.useSticky?Iv(j):Ev(j)}else throw Error("non exhaustive match")}})});var o,a,l,c,u;t("misc mapping",function(){o=(0,xe.map)(i,function(v){return v.tokenTypeIdx}),a=(0,xe.map)(i,function(v){var D=v.GROUP;if(D!==ir.Lexer.SKIPPED){if((0,xe.isString)(D))return D;if((0,xe.isUndefined)(D))return!1;throw Error("non exhaustive match")}}),l=(0,xe.map)(i,function(v){var D=v.LONGER_ALT;if(D){var L=(0,xe.isArray)(D)?(0,xe.map)(D,function(H){return(0,xe.indexOf)(i,H)}):[(0,xe.indexOf)(i,D)];return L}}),c=(0,xe.map)(i,function(v){return v.PUSH_MODE}),u=(0,xe.map)(i,function(v){return(0,xe.has)(v,"POP_MODE")})});var g;t("Line Terminator Handling",function(){var v=DY(e.lineTerminatorCharacters);g=(0,xe.map)(i,function(D){return!1}),e.positionTracking!=="onlyOffset"&&(g=(0,xe.map)(i,function(D){if((0,xe.has)(D,"LINE_BREAKS"))return D.LINE_BREAKS;if(xY(D,v)===!1)return(0,Hg.canMatchCharCode)(v,D.PATTERN)}))});var f,h,p,C;t("Misc Mapping #2",function(){f=(0,xe.map)(i,wv),h=(0,xe.map)(s,vY),p=(0,xe.reduce)(i,function(v,D){var L=D.GROUP;return(0,xe.isString)(L)&&L!==ir.Lexer.SKIPPED&&(v[L]=[]),v},{}),C=(0,xe.map)(s,function(v,D){return{pattern:s[D],longerAlt:l[D],canLineTerminator:g[D],isCustom:f[D],short:h[D],group:a[D],push:c[D],pop:u[D],tokenTypeIdx:o[D],tokenType:i[D]}})});var y=!0,B=[];return e.safeMode||t("First Char Optimization",function(){B=(0,xe.reduce)(i,function(v,D,L){if(typeof D.PATTERN=="string"){var H=D.PATTERN.charCodeAt(0),j=yv(H);mv(v,j,C[L])}else if((0,xe.isArray)(D.START_CHARS_HINT)){var $;(0,xe.forEach)(D.START_CHARS_HINT,function(W){var Z=typeof W=="string"?W.charCodeAt(0):W,A=yv(Z);$!==A&&($=A,mv(v,A,C[L]))})}else if((0,xe.isRegExp)(D.PATTERN))if(D.PATTERN.unicode)y=!1,e.ensureOptimizations&&(0,xe.PRINT_ERROR)(""+Hg.failedOptimizationPrefixMsg+(" Unable to analyze < "+D.PATTERN.toString()+` > pattern. +`)+` The regexp unicode flag is not currently supported by the regexp-to-ast library. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);else{var V=(0,Hg.getOptimizedStartCodesIndices)(D.PATTERN,e.ensureOptimizations);(0,xe.isEmpty)(V)&&(y=!1),(0,xe.forEach)(V,function(W){mv(v,W,C[L])})}else e.ensureOptimizations&&(0,xe.PRINT_ERROR)(""+Hg.failedOptimizationPrefixMsg+(" TokenType: <"+D.name+`> is using a custom token pattern without providing parameter. +`)+` This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`),y=!1;return v},[])}),t("ArrayPacking",function(){B=(0,xe.packArray)(B)}),{emptyGroups:p,patternIdxToConfig:C,charCodeToPatternIdxToConfig:B,hasCustom:n,canBeOptimized:y}}Ve.analyzeTokenTypes=Kme;function Ume(r,e){var t=[],i=CY(r);t=t.concat(i.errors);var n=mY(i.valid),s=n.valid;return t=t.concat(n.errors),t=t.concat(Hme(s)),t=t.concat(QY(s)),t=t.concat(bY(s,e)),t=t.concat(SY(s)),t}Ve.validatePatterns=Ume;function Hme(r){var e=[],t=(0,xe.filter)(r,function(i){return(0,xe.isRegExp)(i[So])});return e=e.concat(EY(t)),e=e.concat(yY(t)),e=e.concat(wY(t)),e=e.concat(BY(t)),e=e.concat(IY(t)),e}function CY(r){var e=(0,xe.filter)(r,function(n){return!(0,xe.has)(n,So)}),t=(0,xe.map)(e,function(n){return{message:"Token Type: ->"+n.name+"<- missing static 'PATTERN' property",type:ir.LexerDefinitionErrorType.MISSING_PATTERN,tokenTypes:[n]}}),i=(0,xe.difference)(r,e);return{errors:t,valid:i}}Ve.findMissingPatterns=CY;function mY(r){var e=(0,xe.filter)(r,function(n){var s=n[So];return!(0,xe.isRegExp)(s)&&!(0,xe.isFunction)(s)&&!(0,xe.has)(s,"exec")&&!(0,xe.isString)(s)}),t=(0,xe.map)(e,function(n){return{message:"Token Type: ->"+n.name+"<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",type:ir.LexerDefinitionErrorType.INVALID_PATTERN,tokenTypes:[n]}}),i=(0,xe.difference)(r,e);return{errors:t,valid:i}}Ve.findInvalidPatterns=mY;var Gme=/[^\\][\$]/;function EY(r){var e=function(n){hY(s,n);function s(){var o=n!==null&&n.apply(this,arguments)||this;return o.found=!1,o}return s.prototype.visitEndAnchor=function(o){this.found=!0},s}(pY.BaseRegExpVisitor),t=(0,xe.filter)(r,function(n){var s=n[So];try{var o=(0,dY.getRegExpAst)(s),a=new e;return a.visit(o),a.found}catch{return Gme.test(s.source)}}),i=(0,xe.map)(t,function(n){return{message:`Unexpected RegExp Anchor Error: + Token Type: ->`+n.name+`<- static 'PATTERN' cannot contain end of input anchor '$' + See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:ir.LexerDefinitionErrorType.EOI_ANCHOR_FOUND,tokenTypes:[n]}});return i}Ve.findEndOfInputAnchor=EY;function IY(r){var e=(0,xe.filter)(r,function(i){var n=i[So];return n.test("")}),t=(0,xe.map)(e,function(i){return{message:"Token Type: ->"+i.name+"<- static 'PATTERN' must not match an empty string",type:ir.LexerDefinitionErrorType.EMPTY_MATCH_PATTERN,tokenTypes:[i]}});return t}Ve.findEmptyMatchRegExps=IY;var Yme=/[^\\[][\^]|^\^/;function yY(r){var e=function(n){hY(s,n);function s(){var o=n!==null&&n.apply(this,arguments)||this;return o.found=!1,o}return s.prototype.visitStartAnchor=function(o){this.found=!0},s}(pY.BaseRegExpVisitor),t=(0,xe.filter)(r,function(n){var s=n[So];try{var o=(0,dY.getRegExpAst)(s),a=new e;return a.visit(o),a.found}catch{return Yme.test(s.source)}}),i=(0,xe.map)(t,function(n){return{message:`Unexpected RegExp Anchor Error: + Token Type: ->`+n.name+`<- static 'PATTERN' cannot contain start of input anchor '^' + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:ir.LexerDefinitionErrorType.SOI_ANCHOR_FOUND,tokenTypes:[n]}});return i}Ve.findStartOfInputAnchor=yY;function wY(r){var e=(0,xe.filter)(r,function(i){var n=i[So];return n instanceof RegExp&&(n.multiline||n.global)}),t=(0,xe.map)(e,function(i){return{message:"Token Type: ->"+i.name+"<- static 'PATTERN' may NOT contain global('g') or multiline('m')",type:ir.LexerDefinitionErrorType.UNSUPPORTED_FLAGS_FOUND,tokenTypes:[i]}});return t}Ve.findUnsupportedFlags=wY;function BY(r){var e=[],t=(0,xe.map)(r,function(s){return(0,xe.reduce)(r,function(o,a){return s.PATTERN.source===a.PATTERN.source&&!(0,xe.contains)(e,a)&&a.PATTERN!==ir.Lexer.NA&&(e.push(a),o.push(a)),o},[])});t=(0,xe.compact)(t);var i=(0,xe.filter)(t,function(s){return s.length>1}),n=(0,xe.map)(i,function(s){var o=(0,xe.map)(s,function(l){return l.name}),a=(0,xe.first)(s).PATTERN;return{message:"The same RegExp pattern ->"+a+"<-"+("has been used in all of the following Token Types: "+o.join(", ")+" <-"),type:ir.LexerDefinitionErrorType.DUPLICATE_PATTERNS_FOUND,tokenTypes:s}});return n}Ve.findDuplicatePatterns=BY;function QY(r){var e=(0,xe.filter)(r,function(i){if(!(0,xe.has)(i,"GROUP"))return!1;var n=i.GROUP;return n!==ir.Lexer.SKIPPED&&n!==ir.Lexer.NA&&!(0,xe.isString)(n)}),t=(0,xe.map)(e,function(i){return{message:"Token Type: ->"+i.name+"<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",type:ir.LexerDefinitionErrorType.INVALID_GROUP_TYPE_FOUND,tokenTypes:[i]}});return t}Ve.findInvalidGroupType=QY;function bY(r,e){var t=(0,xe.filter)(r,function(n){return n.PUSH_MODE!==void 0&&!(0,xe.contains)(e,n.PUSH_MODE)}),i=(0,xe.map)(t,function(n){var s="Token Type: ->"+n.name+"<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->"+n.PUSH_MODE+"<-which does not exist";return{message:s,type:ir.LexerDefinitionErrorType.PUSH_MODE_DOES_NOT_EXIST,tokenTypes:[n]}});return i}Ve.findModesThatDoNotExist=bY;function SY(r){var e=[],t=(0,xe.reduce)(r,function(i,n,s){var o=n.PATTERN;return o===ir.Lexer.NA||((0,xe.isString)(o)?i.push({str:o,idx:s,tokenType:n}):(0,xe.isRegExp)(o)&&qme(o)&&i.push({str:o.source,idx:s,tokenType:n})),i},[]);return(0,xe.forEach)(r,function(i,n){(0,xe.forEach)(t,function(s){var o=s.str,a=s.idx,l=s.tokenType;if(n"+i.name+"<-")+`in the lexer's definition. +See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;e.push({message:c,type:ir.LexerDefinitionErrorType.UNREACHABLE_PATTERN,tokenTypes:[i,l]})}})}),e}Ve.findUnreachablePatterns=SY;function jme(r,e){if((0,xe.isRegExp)(e)){var t=e.exec(r);return t!==null&&t.index===0}else{if((0,xe.isFunction)(e))return e(r,0,[],{});if((0,xe.has)(e,"exec"))return e.exec(r,0,[],{});if(typeof e=="string")return e===r;throw Error("non exhaustive match")}}function qme(r){var e=[".","\\","[","]","|","^","$","(",")","?","*","+","{"];return(0,xe.find)(e,function(t){return r.source.indexOf(t)!==-1})===void 0}function Ev(r){var e=r.ignoreCase?"i":"";return new RegExp("^(?:"+r.source+")",e)}Ve.addStartOfInput=Ev;function Iv(r){var e=r.ignoreCase?"iy":"y";return new RegExp(""+r.source,e)}Ve.addStickyFlag=Iv;function Jme(r,e,t){var i=[];return(0,xe.has)(r,Ve.DEFAULT_MODE)||i.push({message:"A MultiMode Lexer cannot be initialized without a <"+Ve.DEFAULT_MODE+`> property in its definition +`,type:ir.LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),(0,xe.has)(r,Ve.MODES)||i.push({message:"A MultiMode Lexer cannot be initialized without a <"+Ve.MODES+`> property in its definition +`,type:ir.LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),(0,xe.has)(r,Ve.MODES)&&(0,xe.has)(r,Ve.DEFAULT_MODE)&&!(0,xe.has)(r.modes,r.defaultMode)&&i.push({message:"A MultiMode Lexer cannot be initialized with a "+Ve.DEFAULT_MODE+": <"+r.defaultMode+`>which does not exist +`,type:ir.LexerDefinitionErrorType.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),(0,xe.has)(r,Ve.MODES)&&(0,xe.forEach)(r.modes,function(n,s){(0,xe.forEach)(n,function(o,a){(0,xe.isUndefined)(o)&&i.push({message:"A Lexer cannot be initialized using an undefined Token Type. Mode:"+("<"+s+"> at index: <"+a+`> +`),type:ir.LexerDefinitionErrorType.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED})})}),i}Ve.performRuntimeChecks=Jme;function Wme(r,e,t){var i=[],n=!1,s=(0,xe.compact)((0,xe.flatten)((0,xe.mapValues)(r.modes,function(l){return l}))),o=(0,xe.reject)(s,function(l){return l[So]===ir.Lexer.NA}),a=DY(t);return e&&(0,xe.forEach)(o,function(l){var c=xY(l,a);if(c!==!1){var u=PY(l,c),g={message:u,type:c.issue,tokenType:l};i.push(g)}else(0,xe.has)(l,"LINE_BREAKS")?l.LINE_BREAKS===!0&&(n=!0):(0,Hg.canMatchCharCode)(a,l.PATTERN)&&(n=!0)}),e&&!n&&i.push({message:`Warning: No LINE_BREAKS Found. + This Lexer has been defined to track line and column information, + But none of the Token Types can be identified as matching a line terminator. + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS + for details.`,type:ir.LexerDefinitionErrorType.NO_LINE_BREAKS_FLAGS}),i}Ve.performWarningRuntimeChecks=Wme;function zme(r){var e={},t=(0,xe.keys)(r);return(0,xe.forEach)(t,function(i){var n=r[i];if((0,xe.isArray)(n))e[i]=[];else throw Error("non exhaustive match")}),e}Ve.cloneEmptyGroups=zme;function wv(r){var e=r.PATTERN;if((0,xe.isRegExp)(e))return!1;if((0,xe.isFunction)(e))return!0;if((0,xe.has)(e,"exec"))return!0;if((0,xe.isString)(e))return!1;throw Error("non exhaustive match")}Ve.isCustomPattern=wv;function vY(r){return(0,xe.isString)(r)&&r.length===1?r.charCodeAt(0):!1}Ve.isShortPattern=vY;Ve.LineTerminatorOptimizedTester={test:function(r){for(var e=r.length,t=this.lastIndex;t Token Type +`)+(" Root cause: "+e.errMsg+`. +`)+" For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR";if(e.issue===ir.LexerDefinitionErrorType.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the option. +`+(" The problem is in the <"+r.name+`> Token Type +`)+" For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK";throw Error("non exhaustive match")}Ve.buildLineBreakIssueMessage=PY;function DY(r){var e=(0,xe.map)(r,function(t){return(0,xe.isString)(t)&&t.length>0?t.charCodeAt(0):t});return e}function mv(r,e,t){r[e]===void 0?r[e]=[t]:r[e].push(t)}Ve.minOptimizationVal=256;var qI=[];function yv(r){return r255?255+~~(r/255):r}}});var Gg=w(Nt=>{"use strict";Object.defineProperty(Nt,"__esModule",{value:!0});Nt.isTokenType=Nt.hasExtendingTokensTypesMapProperty=Nt.hasExtendingTokensTypesProperty=Nt.hasCategoriesProperty=Nt.hasShortKeyProperty=Nt.singleAssignCategoriesToksMap=Nt.assignCategoriesMapProp=Nt.assignCategoriesTokensProp=Nt.assignTokenDefaultProps=Nt.expandCategories=Nt.augmentTokenTypes=Nt.tokenIdxToClass=Nt.tokenShortNameIdx=Nt.tokenStructuredMatcherNoCategories=Nt.tokenStructuredMatcher=void 0;var _r=Gt();function Xme(r,e){var t=r.tokenTypeIdx;return t===e.tokenTypeIdx?!0:e.isParent===!0&&e.categoryMatchesMap[t]===!0}Nt.tokenStructuredMatcher=Xme;function _me(r,e){return r.tokenTypeIdx===e.tokenTypeIdx}Nt.tokenStructuredMatcherNoCategories=_me;Nt.tokenShortNameIdx=1;Nt.tokenIdxToClass={};function Zme(r){var e=kY(r);RY(e),NY(e),FY(e),(0,_r.forEach)(e,function(t){t.isParent=t.categoryMatches.length>0})}Nt.augmentTokenTypes=Zme;function kY(r){for(var e=(0,_r.cloneArr)(r),t=r,i=!0;i;){t=(0,_r.compact)((0,_r.flatten)((0,_r.map)(t,function(s){return s.CATEGORIES})));var n=(0,_r.difference)(t,e);e=e.concat(n),(0,_r.isEmpty)(n)?i=!1:t=n}return e}Nt.expandCategories=kY;function RY(r){(0,_r.forEach)(r,function(e){LY(e)||(Nt.tokenIdxToClass[Nt.tokenShortNameIdx]=e,e.tokenTypeIdx=Nt.tokenShortNameIdx++),Bv(e)&&!(0,_r.isArray)(e.CATEGORIES)&&(e.CATEGORIES=[e.CATEGORIES]),Bv(e)||(e.CATEGORIES=[]),TY(e)||(e.categoryMatches=[]),OY(e)||(e.categoryMatchesMap={})})}Nt.assignTokenDefaultProps=RY;function FY(r){(0,_r.forEach)(r,function(e){e.categoryMatches=[],(0,_r.forEach)(e.categoryMatchesMap,function(t,i){e.categoryMatches.push(Nt.tokenIdxToClass[i].tokenTypeIdx)})})}Nt.assignCategoriesTokensProp=FY;function NY(r){(0,_r.forEach)(r,function(e){Qv([],e)})}Nt.assignCategoriesMapProp=NY;function Qv(r,e){(0,_r.forEach)(r,function(t){e.categoryMatchesMap[t.tokenTypeIdx]=!0}),(0,_r.forEach)(e.CATEGORIES,function(t){var i=r.concat(e);(0,_r.contains)(i,t)||Qv(i,t)})}Nt.singleAssignCategoriesToksMap=Qv;function LY(r){return(0,_r.has)(r,"tokenTypeIdx")}Nt.hasShortKeyProperty=LY;function Bv(r){return(0,_r.has)(r,"CATEGORIES")}Nt.hasCategoriesProperty=Bv;function TY(r){return(0,_r.has)(r,"categoryMatches")}Nt.hasExtendingTokensTypesProperty=TY;function OY(r){return(0,_r.has)(r,"categoryMatchesMap")}Nt.hasExtendingTokensTypesMapProperty=OY;function $me(r){return(0,_r.has)(r,"tokenTypeIdx")}Nt.isTokenType=$me});var bv=w(JI=>{"use strict";Object.defineProperty(JI,"__esModule",{value:!0});JI.defaultLexerErrorProvider=void 0;JI.defaultLexerErrorProvider={buildUnableToPopLexerModeMessage:function(r){return"Unable to pop Lexer Mode after encountering Token ->"+r.image+"<- The Mode Stack is empty"},buildUnexpectedCharactersMessage:function(r,e,t,i,n){return"unexpected character: ->"+r.charAt(e)+"<- at offset: "+e+","+(" skipped "+t+" characters.")}}});var gd=w(nc=>{"use strict";Object.defineProperty(nc,"__esModule",{value:!0});nc.Lexer=nc.LexerDefinitionErrorType=void 0;var zs=Cv(),nr=Gt(),eEe=Gg(),tEe=bv(),rEe=GI(),iEe;(function(r){r[r.MISSING_PATTERN=0]="MISSING_PATTERN",r[r.INVALID_PATTERN=1]="INVALID_PATTERN",r[r.EOI_ANCHOR_FOUND=2]="EOI_ANCHOR_FOUND",r[r.UNSUPPORTED_FLAGS_FOUND=3]="UNSUPPORTED_FLAGS_FOUND",r[r.DUPLICATE_PATTERNS_FOUND=4]="DUPLICATE_PATTERNS_FOUND",r[r.INVALID_GROUP_TYPE_FOUND=5]="INVALID_GROUP_TYPE_FOUND",r[r.PUSH_MODE_DOES_NOT_EXIST=6]="PUSH_MODE_DOES_NOT_EXIST",r[r.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE=7]="MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE",r[r.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY=8]="MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY",r[r.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST=9]="MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST",r[r.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED=10]="LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED",r[r.SOI_ANCHOR_FOUND=11]="SOI_ANCHOR_FOUND",r[r.EMPTY_MATCH_PATTERN=12]="EMPTY_MATCH_PATTERN",r[r.NO_LINE_BREAKS_FLAGS=13]="NO_LINE_BREAKS_FLAGS",r[r.UNREACHABLE_PATTERN=14]="UNREACHABLE_PATTERN",r[r.IDENTIFY_TERMINATOR=15]="IDENTIFY_TERMINATOR",r[r.CUSTOM_LINE_BREAK=16]="CUSTOM_LINE_BREAK"})(iEe=nc.LexerDefinitionErrorType||(nc.LexerDefinitionErrorType={}));var fd={deferDefinitionErrorsHandling:!1,positionTracking:"full",lineTerminatorsPattern:/\n|\r\n?/g,lineTerminatorCharacters:[` +`,"\r"],ensureOptimizations:!1,safeMode:!1,errorMessageProvider:tEe.defaultLexerErrorProvider,traceInitPerf:!1,skipValidations:!1};Object.freeze(fd);var nEe=function(){function r(e,t){var i=this;if(t===void 0&&(t=fd),this.lexerDefinition=e,this.lexerDefinitionErrors=[],this.lexerDefinitionWarning=[],this.patternIdxToConfig={},this.charCodeToPatternIdxToConfig={},this.modes=[],this.emptyGroups={},this.config=void 0,this.trackStartLines=!0,this.trackEndLines=!0,this.hasCustom=!1,this.canModeBeOptimized={},typeof t=="boolean")throw Error(`The second argument to the Lexer constructor is now an ILexerConfig Object. +a boolean 2nd argument is no longer supported`);this.config=(0,nr.merge)(fd,t);var n=this.config.traceInitPerf;n===!0?(this.traceInitMaxIdent=1/0,this.traceInitPerf=!0):typeof n=="number"&&(this.traceInitMaxIdent=n,this.traceInitPerf=!0),this.traceInitIndent=-1,this.TRACE_INIT("Lexer Constructor",function(){var s,o=!0;i.TRACE_INIT("Lexer Config handling",function(){if(i.config.lineTerminatorsPattern===fd.lineTerminatorsPattern)i.config.lineTerminatorsPattern=zs.LineTerminatorOptimizedTester;else if(i.config.lineTerminatorCharacters===fd.lineTerminatorCharacters)throw Error(`Error: Missing property on the Lexer config. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS`);if(t.safeMode&&t.ensureOptimizations)throw Error('"safeMode" and "ensureOptimizations" flags are mutually exclusive.');i.trackStartLines=/full|onlyStart/i.test(i.config.positionTracking),i.trackEndLines=/full/i.test(i.config.positionTracking),(0,nr.isArray)(e)?(s={modes:{}},s.modes[zs.DEFAULT_MODE]=(0,nr.cloneArr)(e),s[zs.DEFAULT_MODE]=zs.DEFAULT_MODE):(o=!1,s=(0,nr.cloneObj)(e))}),i.config.skipValidations===!1&&(i.TRACE_INIT("performRuntimeChecks",function(){i.lexerDefinitionErrors=i.lexerDefinitionErrors.concat((0,zs.performRuntimeChecks)(s,i.trackStartLines,i.config.lineTerminatorCharacters))}),i.TRACE_INIT("performWarningRuntimeChecks",function(){i.lexerDefinitionWarning=i.lexerDefinitionWarning.concat((0,zs.performWarningRuntimeChecks)(s,i.trackStartLines,i.config.lineTerminatorCharacters))})),s.modes=s.modes?s.modes:{},(0,nr.forEach)(s.modes,function(u,g){s.modes[g]=(0,nr.reject)(u,function(f){return(0,nr.isUndefined)(f)})});var a=(0,nr.keys)(s.modes);if((0,nr.forEach)(s.modes,function(u,g){i.TRACE_INIT("Mode: <"+g+"> processing",function(){if(i.modes.push(g),i.config.skipValidations===!1&&i.TRACE_INIT("validatePatterns",function(){i.lexerDefinitionErrors=i.lexerDefinitionErrors.concat((0,zs.validatePatterns)(u,a))}),(0,nr.isEmpty)(i.lexerDefinitionErrors)){(0,eEe.augmentTokenTypes)(u);var f;i.TRACE_INIT("analyzeTokenTypes",function(){f=(0,zs.analyzeTokenTypes)(u,{lineTerminatorCharacters:i.config.lineTerminatorCharacters,positionTracking:t.positionTracking,ensureOptimizations:t.ensureOptimizations,safeMode:t.safeMode,tracer:i.TRACE_INIT.bind(i)})}),i.patternIdxToConfig[g]=f.patternIdxToConfig,i.charCodeToPatternIdxToConfig[g]=f.charCodeToPatternIdxToConfig,i.emptyGroups=(0,nr.merge)(i.emptyGroups,f.emptyGroups),i.hasCustom=f.hasCustom||i.hasCustom,i.canModeBeOptimized[g]=f.canBeOptimized}})}),i.defaultMode=s.defaultMode,!(0,nr.isEmpty)(i.lexerDefinitionErrors)&&!i.config.deferDefinitionErrorsHandling){var l=(0,nr.map)(i.lexerDefinitionErrors,function(u){return u.message}),c=l.join(`----------------------- +`);throw new Error(`Errors detected in definition of Lexer: +`+c)}(0,nr.forEach)(i.lexerDefinitionWarning,function(u){(0,nr.PRINT_WARNING)(u.message)}),i.TRACE_INIT("Choosing sub-methods implementations",function(){if(zs.SUPPORT_STICKY?(i.chopInput=nr.IDENTITY,i.match=i.matchWithTest):(i.updateLastIndex=nr.NOOP,i.match=i.matchWithExec),o&&(i.handleModes=nr.NOOP),i.trackStartLines===!1&&(i.computeNewColumn=nr.IDENTITY),i.trackEndLines===!1&&(i.updateTokenEndLineColumnLocation=nr.NOOP),/full/i.test(i.config.positionTracking))i.createTokenInstance=i.createFullToken;else if(/onlyStart/i.test(i.config.positionTracking))i.createTokenInstance=i.createStartOnlyToken;else if(/onlyOffset/i.test(i.config.positionTracking))i.createTokenInstance=i.createOffsetOnlyToken;else throw Error('Invalid config option: "'+i.config.positionTracking+'"');i.hasCustom?(i.addToken=i.addTokenUsingPush,i.handlePayload=i.handlePayloadWithCustom):(i.addToken=i.addTokenUsingMemberAccess,i.handlePayload=i.handlePayloadNoCustom)}),i.TRACE_INIT("Failed Optimization Warnings",function(){var u=(0,nr.reduce)(i.canModeBeOptimized,function(g,f,h){return f===!1&&g.push(h),g},[]);if(t.ensureOptimizations&&!(0,nr.isEmpty)(u))throw Error("Lexer Modes: < "+u.join(", ")+` > cannot be optimized. + Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode. + Or inspect the console log for details on how to resolve these issues.`)}),i.TRACE_INIT("clearRegExpParserCache",function(){(0,rEe.clearRegExpParserCache)()}),i.TRACE_INIT("toFastProperties",function(){(0,nr.toFastProperties)(i)})})}return r.prototype.tokenize=function(e,t){if(t===void 0&&(t=this.defaultMode),!(0,nr.isEmpty)(this.lexerDefinitionErrors)){var i=(0,nr.map)(this.lexerDefinitionErrors,function(o){return o.message}),n=i.join(`----------------------- +`);throw new Error(`Unable to Tokenize because Errors detected in definition of Lexer: +`+n)}var s=this.tokenizeInternal(e,t);return s},r.prototype.tokenizeInternal=function(e,t){var i=this,n,s,o,a,l,c,u,g,f,h,p,C,y,B,v,D,L=e,H=L.length,j=0,$=0,V=this.hasCustom?0:Math.floor(e.length/10),W=new Array(V),Z=[],A=this.trackStartLines?1:void 0,ae=this.trackStartLines?1:void 0,ge=(0,zs.cloneEmptyGroups)(this.emptyGroups),re=this.trackStartLines,O=this.config.lineTerminatorsPattern,F=0,ue=[],he=[],ke=[],Fe=[];Object.freeze(Fe);var Ne=void 0;function oe(){return ue}function le(pr){var Ei=(0,zs.charCodeToOptimizedIndex)(pr),_n=he[Ei];return _n===void 0?Fe:_n}var we=function(pr){if(ke.length===1&&pr.tokenType.PUSH_MODE===void 0){var Ei=i.config.errorMessageProvider.buildUnableToPopLexerModeMessage(pr);Z.push({offset:pr.startOffset,line:pr.startLine!==void 0?pr.startLine:void 0,column:pr.startColumn!==void 0?pr.startColumn:void 0,length:pr.image.length,message:Ei})}else{ke.pop();var _n=(0,nr.last)(ke);ue=i.patternIdxToConfig[_n],he=i.charCodeToPatternIdxToConfig[_n],F=ue.length;var oa=i.canModeBeOptimized[_n]&&i.config.safeMode===!1;he&&oa?Ne=le:Ne=oe}};function fe(pr){ke.push(pr),he=this.charCodeToPatternIdxToConfig[pr],ue=this.patternIdxToConfig[pr],F=ue.length,F=ue.length;var Ei=this.canModeBeOptimized[pr]&&this.config.safeMode===!1;he&&Ei?Ne=le:Ne=oe}fe.call(this,t);for(var Ae;jc.length){c=a,u=g,Ae=tt;break}}}break}}if(c!==null){if(f=c.length,h=Ae.group,h!==void 0&&(p=Ae.tokenTypeIdx,C=this.createTokenInstance(c,j,p,Ae.tokenType,A,ae,f),this.handlePayload(C,u),h===!1?$=this.addToken(W,$,C):ge[h].push(C)),e=this.chopInput(e,f),j=j+f,ae=this.computeNewColumn(ae,f),re===!0&&Ae.canLineTerminator===!0){var It=0,Or=void 0,ii=void 0;O.lastIndex=0;do Or=O.test(c),Or===!0&&(ii=O.lastIndex-1,It++);while(Or===!0);It!==0&&(A=A+It,ae=f-ii,this.updateTokenEndLineColumnLocation(C,h,ii,It,A,ae,f))}this.handleModes(Ae,we,fe,C)}else{for(var gi=j,hr=A,fi=ae,ni=!1;!ni&&j <"+e+">");var n=(0,nr.timer)(t),s=n.time,o=n.value,a=s>10?console.warn:console.log;return this.traceInitIndent time: "+s+"ms"),this.traceInitIndent--,o}else return t()},r.SKIPPED="This marks a skipped Token pattern, this means each token identified by it willbe consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.",r.NA=/NOT_APPLICABLE/,r}();nc.Lexer=nEe});var SA=w(Qi=>{"use strict";Object.defineProperty(Qi,"__esModule",{value:!0});Qi.tokenMatcher=Qi.createTokenInstance=Qi.EOF=Qi.createToken=Qi.hasTokenLabel=Qi.tokenName=Qi.tokenLabel=void 0;var Vs=Gt(),sEe=gd(),Sv=Gg();function oEe(r){return JY(r)?r.LABEL:r.name}Qi.tokenLabel=oEe;function aEe(r){return r.name}Qi.tokenName=aEe;function JY(r){return(0,Vs.isString)(r.LABEL)&&r.LABEL!==""}Qi.hasTokenLabel=JY;var AEe="parent",MY="categories",KY="label",UY="group",HY="push_mode",GY="pop_mode",YY="longer_alt",jY="line_breaks",qY="start_chars_hint";function WY(r){return lEe(r)}Qi.createToken=WY;function lEe(r){var e=r.pattern,t={};if(t.name=r.name,(0,Vs.isUndefined)(e)||(t.PATTERN=e),(0,Vs.has)(r,AEe))throw`The parent property is no longer supported. +See: https://github.com/chevrotain/chevrotain/issues/564#issuecomment-349062346 for details.`;return(0,Vs.has)(r,MY)&&(t.CATEGORIES=r[MY]),(0,Sv.augmentTokenTypes)([t]),(0,Vs.has)(r,KY)&&(t.LABEL=r[KY]),(0,Vs.has)(r,UY)&&(t.GROUP=r[UY]),(0,Vs.has)(r,GY)&&(t.POP_MODE=r[GY]),(0,Vs.has)(r,HY)&&(t.PUSH_MODE=r[HY]),(0,Vs.has)(r,YY)&&(t.LONGER_ALT=r[YY]),(0,Vs.has)(r,jY)&&(t.LINE_BREAKS=r[jY]),(0,Vs.has)(r,qY)&&(t.START_CHARS_HINT=r[qY]),t}Qi.EOF=WY({name:"EOF",pattern:sEe.Lexer.NA});(0,Sv.augmentTokenTypes)([Qi.EOF]);function cEe(r,e,t,i,n,s,o,a){return{image:e,startOffset:t,endOffset:i,startLine:n,endLine:s,startColumn:o,endColumn:a,tokenTypeIdx:r.tokenTypeIdx,tokenType:r}}Qi.createTokenInstance=cEe;function uEe(r,e){return(0,Sv.tokenStructuredMatcher)(r,e)}Qi.tokenMatcher=uEe});var dn=w(Wt=>{"use strict";var wa=Wt&&Wt.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(Wt,"__esModule",{value:!0});Wt.serializeProduction=Wt.serializeGrammar=Wt.Terminal=Wt.Alternation=Wt.RepetitionWithSeparator=Wt.Repetition=Wt.RepetitionMandatoryWithSeparator=Wt.RepetitionMandatory=Wt.Option=Wt.Alternative=Wt.Rule=Wt.NonTerminal=Wt.AbstractProduction=void 0;var Ar=Gt(),gEe=SA(),vo=function(){function r(e){this._definition=e}return Object.defineProperty(r.prototype,"definition",{get:function(){return this._definition},set:function(e){this._definition=e},enumerable:!1,configurable:!0}),r.prototype.accept=function(e){e.visit(this),(0,Ar.forEach)(this.definition,function(t){t.accept(e)})},r}();Wt.AbstractProduction=vo;var zY=function(r){wa(e,r);function e(t){var i=r.call(this,[])||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return Object.defineProperty(e.prototype,"definition",{get:function(){return this.referencedRule!==void 0?this.referencedRule.definition:[]},set:function(t){},enumerable:!1,configurable:!0}),e.prototype.accept=function(t){t.visit(this)},e}(vo);Wt.NonTerminal=zY;var VY=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.orgText="",(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.Rule=VY;var XY=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.ignoreAmbiguities=!1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.Alternative=XY;var _Y=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.Option=_Y;var ZY=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.RepetitionMandatory=ZY;var $Y=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.RepetitionMandatoryWithSeparator=$Y;var ej=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.Repetition=ej;var tj=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return e}(vo);Wt.RepetitionWithSeparator=tj;var rj=function(r){wa(e,r);function e(t){var i=r.call(this,t.definition)||this;return i.idx=1,i.ignoreAmbiguities=!1,i.hasPredicates=!1,(0,Ar.assign)(i,(0,Ar.pick)(t,function(n){return n!==void 0})),i}return Object.defineProperty(e.prototype,"definition",{get:function(){return this._definition},set:function(t){this._definition=t},enumerable:!1,configurable:!0}),e}(vo);Wt.Alternation=rj;var WI=function(){function r(e){this.idx=1,(0,Ar.assign)(this,(0,Ar.pick)(e,function(t){return t!==void 0}))}return r.prototype.accept=function(e){e.visit(this)},r}();Wt.Terminal=WI;function fEe(r){return(0,Ar.map)(r,hd)}Wt.serializeGrammar=fEe;function hd(r){function e(s){return(0,Ar.map)(s,hd)}if(r instanceof zY){var t={type:"NonTerminal",name:r.nonTerminalName,idx:r.idx};return(0,Ar.isString)(r.label)&&(t.label=r.label),t}else{if(r instanceof XY)return{type:"Alternative",definition:e(r.definition)};if(r instanceof _Y)return{type:"Option",idx:r.idx,definition:e(r.definition)};if(r instanceof ZY)return{type:"RepetitionMandatory",idx:r.idx,definition:e(r.definition)};if(r instanceof $Y)return{type:"RepetitionMandatoryWithSeparator",idx:r.idx,separator:hd(new WI({terminalType:r.separator})),definition:e(r.definition)};if(r instanceof tj)return{type:"RepetitionWithSeparator",idx:r.idx,separator:hd(new WI({terminalType:r.separator})),definition:e(r.definition)};if(r instanceof ej)return{type:"Repetition",idx:r.idx,definition:e(r.definition)};if(r instanceof rj)return{type:"Alternation",idx:r.idx,definition:e(r.definition)};if(r instanceof WI){var i={type:"Terminal",name:r.terminalType.name,label:(0,gEe.tokenLabel)(r.terminalType),idx:r.idx};(0,Ar.isString)(r.label)&&(i.terminalLabel=r.label);var n=r.terminalType.PATTERN;return r.terminalType.PATTERN&&(i.pattern=(0,Ar.isRegExp)(n)?n.source:n),i}else{if(r instanceof VY)return{type:"Rule",name:r.name,orgText:r.orgText,definition:e(r.definition)};throw Error("non exhaustive match")}}}Wt.serializeProduction=hd});var VI=w(zI=>{"use strict";Object.defineProperty(zI,"__esModule",{value:!0});zI.RestWalker=void 0;var vv=Gt(),Cn=dn(),hEe=function(){function r(){}return r.prototype.walk=function(e,t){var i=this;t===void 0&&(t=[]),(0,vv.forEach)(e.definition,function(n,s){var o=(0,vv.drop)(e.definition,s+1);if(n instanceof Cn.NonTerminal)i.walkProdRef(n,o,t);else if(n instanceof Cn.Terminal)i.walkTerminal(n,o,t);else if(n instanceof Cn.Alternative)i.walkFlat(n,o,t);else if(n instanceof Cn.Option)i.walkOption(n,o,t);else if(n instanceof Cn.RepetitionMandatory)i.walkAtLeastOne(n,o,t);else if(n instanceof Cn.RepetitionMandatoryWithSeparator)i.walkAtLeastOneSep(n,o,t);else if(n instanceof Cn.RepetitionWithSeparator)i.walkManySep(n,o,t);else if(n instanceof Cn.Repetition)i.walkMany(n,o,t);else if(n instanceof Cn.Alternation)i.walkOr(n,o,t);else throw Error("non exhaustive match")})},r.prototype.walkTerminal=function(e,t,i){},r.prototype.walkProdRef=function(e,t,i){},r.prototype.walkFlat=function(e,t,i){var n=t.concat(i);this.walk(e,n)},r.prototype.walkOption=function(e,t,i){var n=t.concat(i);this.walk(e,n)},r.prototype.walkAtLeastOne=function(e,t,i){var n=[new Cn.Option({definition:e.definition})].concat(t,i);this.walk(e,n)},r.prototype.walkAtLeastOneSep=function(e,t,i){var n=ij(e,t,i);this.walk(e,n)},r.prototype.walkMany=function(e,t,i){var n=[new Cn.Option({definition:e.definition})].concat(t,i);this.walk(e,n)},r.prototype.walkManySep=function(e,t,i){var n=ij(e,t,i);this.walk(e,n)},r.prototype.walkOr=function(e,t,i){var n=this,s=t.concat(i);(0,vv.forEach)(e.definition,function(o){var a=new Cn.Alternative({definition:[o]});n.walk(a,s)})},r}();zI.RestWalker=hEe;function ij(r,e,t){var i=[new Cn.Option({definition:[new Cn.Terminal({terminalType:r.separator})].concat(r.definition)})],n=i.concat(e,t);return n}});var Yg=w(XI=>{"use strict";Object.defineProperty(XI,"__esModule",{value:!0});XI.GAstVisitor=void 0;var xo=dn(),pEe=function(){function r(){}return r.prototype.visit=function(e){var t=e;switch(t.constructor){case xo.NonTerminal:return this.visitNonTerminal(t);case xo.Alternative:return this.visitAlternative(t);case xo.Option:return this.visitOption(t);case xo.RepetitionMandatory:return this.visitRepetitionMandatory(t);case xo.RepetitionMandatoryWithSeparator:return this.visitRepetitionMandatoryWithSeparator(t);case xo.RepetitionWithSeparator:return this.visitRepetitionWithSeparator(t);case xo.Repetition:return this.visitRepetition(t);case xo.Alternation:return this.visitAlternation(t);case xo.Terminal:return this.visitTerminal(t);case xo.Rule:return this.visitRule(t);default:throw Error("non exhaustive match")}},r.prototype.visitNonTerminal=function(e){},r.prototype.visitAlternative=function(e){},r.prototype.visitOption=function(e){},r.prototype.visitRepetition=function(e){},r.prototype.visitRepetitionMandatory=function(e){},r.prototype.visitRepetitionMandatoryWithSeparator=function(e){},r.prototype.visitRepetitionWithSeparator=function(e){},r.prototype.visitAlternation=function(e){},r.prototype.visitTerminal=function(e){},r.prototype.visitRule=function(e){},r}();XI.GAstVisitor=pEe});var dd=w(Oi=>{"use strict";var dEe=Oi&&Oi.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(Oi,"__esModule",{value:!0});Oi.collectMethods=Oi.DslMethodsCollectorVisitor=Oi.getProductionDslName=Oi.isBranchingProd=Oi.isOptionalProd=Oi.isSequenceProd=void 0;var pd=Gt(),Qr=dn(),CEe=Yg();function mEe(r){return r instanceof Qr.Alternative||r instanceof Qr.Option||r instanceof Qr.Repetition||r instanceof Qr.RepetitionMandatory||r instanceof Qr.RepetitionMandatoryWithSeparator||r instanceof Qr.RepetitionWithSeparator||r instanceof Qr.Terminal||r instanceof Qr.Rule}Oi.isSequenceProd=mEe;function xv(r,e){e===void 0&&(e=[]);var t=r instanceof Qr.Option||r instanceof Qr.Repetition||r instanceof Qr.RepetitionWithSeparator;return t?!0:r instanceof Qr.Alternation?(0,pd.some)(r.definition,function(i){return xv(i,e)}):r instanceof Qr.NonTerminal&&(0,pd.contains)(e,r)?!1:r instanceof Qr.AbstractProduction?(r instanceof Qr.NonTerminal&&e.push(r),(0,pd.every)(r.definition,function(i){return xv(i,e)})):!1}Oi.isOptionalProd=xv;function EEe(r){return r instanceof Qr.Alternation}Oi.isBranchingProd=EEe;function IEe(r){if(r instanceof Qr.NonTerminal)return"SUBRULE";if(r instanceof Qr.Option)return"OPTION";if(r instanceof Qr.Alternation)return"OR";if(r instanceof Qr.RepetitionMandatory)return"AT_LEAST_ONE";if(r instanceof Qr.RepetitionMandatoryWithSeparator)return"AT_LEAST_ONE_SEP";if(r instanceof Qr.RepetitionWithSeparator)return"MANY_SEP";if(r instanceof Qr.Repetition)return"MANY";if(r instanceof Qr.Terminal)return"CONSUME";throw Error("non exhaustive match")}Oi.getProductionDslName=IEe;var nj=function(r){dEe(e,r);function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.separator="-",t.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]},t}return e.prototype.reset=function(){this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}},e.prototype.visitTerminal=function(t){var i=t.terminalType.name+this.separator+"Terminal";(0,pd.has)(this.dslMethods,i)||(this.dslMethods[i]=[]),this.dslMethods[i].push(t)},e.prototype.visitNonTerminal=function(t){var i=t.nonTerminalName+this.separator+"Terminal";(0,pd.has)(this.dslMethods,i)||(this.dslMethods[i]=[]),this.dslMethods[i].push(t)},e.prototype.visitOption=function(t){this.dslMethods.option.push(t)},e.prototype.visitRepetitionWithSeparator=function(t){this.dslMethods.repetitionWithSeparator.push(t)},e.prototype.visitRepetitionMandatory=function(t){this.dslMethods.repetitionMandatory.push(t)},e.prototype.visitRepetitionMandatoryWithSeparator=function(t){this.dslMethods.repetitionMandatoryWithSeparator.push(t)},e.prototype.visitRepetition=function(t){this.dslMethods.repetition.push(t)},e.prototype.visitAlternation=function(t){this.dslMethods.alternation.push(t)},e}(CEe.GAstVisitor);Oi.DslMethodsCollectorVisitor=nj;var _I=new nj;function yEe(r){_I.reset(),r.accept(_I);var e=_I.dslMethods;return _I.reset(),e}Oi.collectMethods=yEe});var Dv=w(Po=>{"use strict";Object.defineProperty(Po,"__esModule",{value:!0});Po.firstForTerminal=Po.firstForBranching=Po.firstForSequence=Po.first=void 0;var ZI=Gt(),sj=dn(),Pv=dd();function $I(r){if(r instanceof sj.NonTerminal)return $I(r.referencedRule);if(r instanceof sj.Terminal)return Aj(r);if((0,Pv.isSequenceProd)(r))return oj(r);if((0,Pv.isBranchingProd)(r))return aj(r);throw Error("non exhaustive match")}Po.first=$I;function oj(r){for(var e=[],t=r.definition,i=0,n=t.length>i,s,o=!0;n&&o;)s=t[i],o=(0,Pv.isOptionalProd)(s),e=e.concat($I(s)),i=i+1,n=t.length>i;return(0,ZI.uniq)(e)}Po.firstForSequence=oj;function aj(r){var e=(0,ZI.map)(r.definition,function(t){return $I(t)});return(0,ZI.uniq)((0,ZI.flatten)(e))}Po.firstForBranching=aj;function Aj(r){return[r.terminalType]}Po.firstForTerminal=Aj});var kv=w(ey=>{"use strict";Object.defineProperty(ey,"__esModule",{value:!0});ey.IN=void 0;ey.IN="_~IN~_"});var fj=w(As=>{"use strict";var wEe=As&&As.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(As,"__esModule",{value:!0});As.buildInProdFollowPrefix=As.buildBetweenProdsFollowPrefix=As.computeAllProdsFollows=As.ResyncFollowsWalker=void 0;var BEe=VI(),QEe=Dv(),lj=Gt(),cj=kv(),bEe=dn(),uj=function(r){wEe(e,r);function e(t){var i=r.call(this)||this;return i.topProd=t,i.follows={},i}return e.prototype.startWalking=function(){return this.walk(this.topProd),this.follows},e.prototype.walkTerminal=function(t,i,n){},e.prototype.walkProdRef=function(t,i,n){var s=gj(t.referencedRule,t.idx)+this.topProd.name,o=i.concat(n),a=new bEe.Alternative({definition:o}),l=(0,QEe.first)(a);this.follows[s]=l},e}(BEe.RestWalker);As.ResyncFollowsWalker=uj;function SEe(r){var e={};return(0,lj.forEach)(r,function(t){var i=new uj(t).startWalking();(0,lj.assign)(e,i)}),e}As.computeAllProdsFollows=SEe;function gj(r,e){return r.name+e+cj.IN}As.buildBetweenProdsFollowPrefix=gj;function vEe(r){var e=r.terminalType.name;return e+r.idx+cj.IN}As.buildInProdFollowPrefix=vEe});var Cd=w(Ba=>{"use strict";Object.defineProperty(Ba,"__esModule",{value:!0});Ba.defaultGrammarValidatorErrorProvider=Ba.defaultGrammarResolverErrorProvider=Ba.defaultParserErrorProvider=void 0;var jg=SA(),xEe=Gt(),Xs=Gt(),Rv=dn(),hj=dd();Ba.defaultParserErrorProvider={buildMismatchTokenMessage:function(r){var e=r.expected,t=r.actual,i=r.previous,n=r.ruleName,s=(0,jg.hasTokenLabel)(e),o=s?"--> "+(0,jg.tokenLabel)(e)+" <--":"token of type --> "+e.name+" <--",a="Expecting "+o+" but found --> '"+t.image+"' <--";return a},buildNotAllInputParsedMessage:function(r){var e=r.firstRedundant,t=r.ruleName;return"Redundant input, expecting EOF but found: "+e.image},buildNoViableAltMessage:function(r){var e=r.expectedPathsPerAlt,t=r.actual,i=r.previous,n=r.customUserDescription,s=r.ruleName,o="Expecting: ",a=(0,Xs.first)(t).image,l=` +but found: '`+a+"'";if(n)return o+n+l;var c=(0,Xs.reduce)(e,function(h,p){return h.concat(p)},[]),u=(0,Xs.map)(c,function(h){return"["+(0,Xs.map)(h,function(p){return(0,jg.tokenLabel)(p)}).join(", ")+"]"}),g=(0,Xs.map)(u,function(h,p){return" "+(p+1)+". "+h}),f=`one of these possible Token sequences: +`+g.join(` +`);return o+f+l},buildEarlyExitMessage:function(r){var e=r.expectedIterationPaths,t=r.actual,i=r.customUserDescription,n=r.ruleName,s="Expecting: ",o=(0,Xs.first)(t).image,a=` +but found: '`+o+"'";if(i)return s+i+a;var l=(0,Xs.map)(e,function(u){return"["+(0,Xs.map)(u,function(g){return(0,jg.tokenLabel)(g)}).join(",")+"]"}),c=`expecting at least one iteration which starts with one of these possible Token sequences:: + `+("<"+l.join(" ,")+">");return s+c+a}};Object.freeze(Ba.defaultParserErrorProvider);Ba.defaultGrammarResolverErrorProvider={buildRuleNotFoundError:function(r,e){var t="Invalid grammar, reference to a rule which is not defined: ->"+e.nonTerminalName+`<- +inside top level rule: ->`+r.name+"<-";return t}};Ba.defaultGrammarValidatorErrorProvider={buildDuplicateFoundError:function(r,e){function t(u){return u instanceof Rv.Terminal?u.terminalType.name:u instanceof Rv.NonTerminal?u.nonTerminalName:""}var i=r.name,n=(0,Xs.first)(e),s=n.idx,o=(0,hj.getProductionDslName)(n),a=t(n),l=s>0,c="->"+o+(l?s:"")+"<- "+(a?"with argument: ->"+a+"<-":"")+` + appears more than once (`+e.length+" times) in the top level rule: ->"+i+`<-. + For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES + `;return c=c.replace(/[ \t]+/g," "),c=c.replace(/\s\s+/g,` +`),c},buildNamespaceConflictError:function(r){var e=`Namespace conflict found in grammar. +`+("The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <"+r.name+`>. +`)+`To resolve this make sure each Terminal and Non-Terminal names are unique +This is easy to accomplish by using the convention that Terminal names start with an uppercase letter +and Non-Terminal names start with a lower case letter.`;return e},buildAlternationPrefixAmbiguityError:function(r){var e=(0,Xs.map)(r.prefixPath,function(n){return(0,jg.tokenLabel)(n)}).join(", "),t=r.alternation.idx===0?"":r.alternation.idx,i="Ambiguous alternatives: <"+r.ambiguityIndices.join(" ,")+`> due to common lookahead prefix +`+("in inside <"+r.topLevelRule.name+`> Rule, +`)+("<"+e+`> may appears as a prefix path in all these alternatives. +`)+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX +For Further details.`;return i},buildAlternationAmbiguityError:function(r){var e=(0,Xs.map)(r.prefixPath,function(n){return(0,jg.tokenLabel)(n)}).join(", "),t=r.alternation.idx===0?"":r.alternation.idx,i="Ambiguous Alternatives Detected: <"+r.ambiguityIndices.join(" ,")+"> in "+(" inside <"+r.topLevelRule.name+`> Rule, +`)+("<"+e+`> may appears as a prefix path in all these alternatives. +`);return i=i+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,i},buildEmptyRepetitionError:function(r){var e=(0,hj.getProductionDslName)(r.repetition);r.repetition.idx!==0&&(e+=r.repetition.idx);var t="The repetition <"+e+"> within Rule <"+r.topLevelRule.name+`> can never consume any tokens. +This could lead to an infinite loop.`;return t},buildTokenNameError:function(r){return"deprecated"},buildEmptyAlternationError:function(r){var e="Ambiguous empty alternative: <"+(r.emptyChoiceIdx+1)+">"+(" in inside <"+r.topLevelRule.name+`> Rule. +`)+"Only the last alternative may be an empty alternative.";return e},buildTooManyAlternativesError:function(r){var e=`An Alternation cannot have more than 256 alternatives: +`+(" inside <"+r.topLevelRule.name+`> Rule. + has `+(r.alternation.definition.length+1)+" alternatives.");return e},buildLeftRecursionError:function(r){var e=r.topLevelRule.name,t=xEe.map(r.leftRecursionPath,function(s){return s.name}),i=e+" --> "+t.concat([e]).join(" --> "),n=`Left Recursion found in grammar. +`+("rule: <"+e+`> can be invoked from itself (directly or indirectly) +`)+(`without consuming any Tokens. The grammar path that causes this is: + `+i+` +`)+` To fix this refactor your grammar to remove the left recursion. +see: https://en.wikipedia.org/wiki/LL_parser#Left_Factoring.`;return n},buildInvalidRuleNameError:function(r){return"deprecated"},buildDuplicateRuleNameError:function(r){var e;r.topLevelRule instanceof Rv.Rule?e=r.topLevelRule.name:e=r.topLevelRule;var t="Duplicate definition, rule: ->"+e+"<- is already defined in the grammar: ->"+r.grammarName+"<-";return t}}});var Cj=w(vA=>{"use strict";var PEe=vA&&vA.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(vA,"__esModule",{value:!0});vA.GastRefResolverVisitor=vA.resolveGrammar=void 0;var DEe=Hn(),pj=Gt(),kEe=Yg();function REe(r,e){var t=new dj(r,e);return t.resolveRefs(),t.errors}vA.resolveGrammar=REe;var dj=function(r){PEe(e,r);function e(t,i){var n=r.call(this)||this;return n.nameToTopRule=t,n.errMsgProvider=i,n.errors=[],n}return e.prototype.resolveRefs=function(){var t=this;(0,pj.forEach)((0,pj.values)(this.nameToTopRule),function(i){t.currTopLevel=i,i.accept(t)})},e.prototype.visitNonTerminal=function(t){var i=this.nameToTopRule[t.nonTerminalName];if(i)t.referencedRule=i;else{var n=this.errMsgProvider.buildRuleNotFoundError(this.currTopLevel,t);this.errors.push({message:n,type:DEe.ParserDefinitionErrorType.UNRESOLVED_SUBRULE_REF,ruleName:this.currTopLevel.name,unresolvedRefName:t.nonTerminalName})}},e}(kEe.GAstVisitor);vA.GastRefResolverVisitor=dj});var Ed=w(Nr=>{"use strict";var sc=Nr&&Nr.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(Nr,"__esModule",{value:!0});Nr.nextPossibleTokensAfter=Nr.possiblePathsFrom=Nr.NextTerminalAfterAtLeastOneSepWalker=Nr.NextTerminalAfterAtLeastOneWalker=Nr.NextTerminalAfterManySepWalker=Nr.NextTerminalAfterManyWalker=Nr.AbstractNextTerminalAfterProductionWalker=Nr.NextAfterTokenWalker=Nr.AbstractNextPossibleTokensWalker=void 0;var mj=VI(),Kt=Gt(),FEe=Dv(),kt=dn(),Ej=function(r){sc(e,r);function e(t,i){var n=r.call(this)||this;return n.topProd=t,n.path=i,n.possibleTokTypes=[],n.nextProductionName="",n.nextProductionOccurrence=0,n.found=!1,n.isAtEndOfPath=!1,n}return e.prototype.startWalking=function(){if(this.found=!1,this.path.ruleStack[0]!==this.topProd.name)throw Error("The path does not start with the walker's top Rule!");return this.ruleStack=(0,Kt.cloneArr)(this.path.ruleStack).reverse(),this.occurrenceStack=(0,Kt.cloneArr)(this.path.occurrenceStack).reverse(),this.ruleStack.pop(),this.occurrenceStack.pop(),this.updateExpectedNext(),this.walk(this.topProd),this.possibleTokTypes},e.prototype.walk=function(t,i){i===void 0&&(i=[]),this.found||r.prototype.walk.call(this,t,i)},e.prototype.walkProdRef=function(t,i,n){if(t.referencedRule.name===this.nextProductionName&&t.idx===this.nextProductionOccurrence){var s=i.concat(n);this.updateExpectedNext(),this.walk(t.referencedRule,s)}},e.prototype.updateExpectedNext=function(){(0,Kt.isEmpty)(this.ruleStack)?(this.nextProductionName="",this.nextProductionOccurrence=0,this.isAtEndOfPath=!0):(this.nextProductionName=this.ruleStack.pop(),this.nextProductionOccurrence=this.occurrenceStack.pop())},e}(mj.RestWalker);Nr.AbstractNextPossibleTokensWalker=Ej;var NEe=function(r){sc(e,r);function e(t,i){var n=r.call(this,t,i)||this;return n.path=i,n.nextTerminalName="",n.nextTerminalOccurrence=0,n.nextTerminalName=n.path.lastTok.name,n.nextTerminalOccurrence=n.path.lastTokOccurrence,n}return e.prototype.walkTerminal=function(t,i,n){if(this.isAtEndOfPath&&t.terminalType.name===this.nextTerminalName&&t.idx===this.nextTerminalOccurrence&&!this.found){var s=i.concat(n),o=new kt.Alternative({definition:s});this.possibleTokTypes=(0,FEe.first)(o),this.found=!0}},e}(Ej);Nr.NextAfterTokenWalker=NEe;var md=function(r){sc(e,r);function e(t,i){var n=r.call(this)||this;return n.topRule=t,n.occurrence=i,n.result={token:void 0,occurrence:void 0,isEndOfRule:void 0},n}return e.prototype.startWalking=function(){return this.walk(this.topRule),this.result},e}(mj.RestWalker);Nr.AbstractNextTerminalAfterProductionWalker=md;var LEe=function(r){sc(e,r);function e(){return r!==null&&r.apply(this,arguments)||this}return e.prototype.walkMany=function(t,i,n){if(t.idx===this.occurrence){var s=(0,Kt.first)(i.concat(n));this.result.isEndOfRule=s===void 0,s instanceof kt.Terminal&&(this.result.token=s.terminalType,this.result.occurrence=s.idx)}else r.prototype.walkMany.call(this,t,i,n)},e}(md);Nr.NextTerminalAfterManyWalker=LEe;var TEe=function(r){sc(e,r);function e(){return r!==null&&r.apply(this,arguments)||this}return e.prototype.walkManySep=function(t,i,n){if(t.idx===this.occurrence){var s=(0,Kt.first)(i.concat(n));this.result.isEndOfRule=s===void 0,s instanceof kt.Terminal&&(this.result.token=s.terminalType,this.result.occurrence=s.idx)}else r.prototype.walkManySep.call(this,t,i,n)},e}(md);Nr.NextTerminalAfterManySepWalker=TEe;var OEe=function(r){sc(e,r);function e(){return r!==null&&r.apply(this,arguments)||this}return e.prototype.walkAtLeastOne=function(t,i,n){if(t.idx===this.occurrence){var s=(0,Kt.first)(i.concat(n));this.result.isEndOfRule=s===void 0,s instanceof kt.Terminal&&(this.result.token=s.terminalType,this.result.occurrence=s.idx)}else r.prototype.walkAtLeastOne.call(this,t,i,n)},e}(md);Nr.NextTerminalAfterAtLeastOneWalker=OEe;var MEe=function(r){sc(e,r);function e(){return r!==null&&r.apply(this,arguments)||this}return e.prototype.walkAtLeastOneSep=function(t,i,n){if(t.idx===this.occurrence){var s=(0,Kt.first)(i.concat(n));this.result.isEndOfRule=s===void 0,s instanceof kt.Terminal&&(this.result.token=s.terminalType,this.result.occurrence=s.idx)}else r.prototype.walkAtLeastOneSep.call(this,t,i,n)},e}(md);Nr.NextTerminalAfterAtLeastOneSepWalker=MEe;function Ij(r,e,t){t===void 0&&(t=[]),t=(0,Kt.cloneArr)(t);var i=[],n=0;function s(c){return c.concat((0,Kt.drop)(r,n+1))}function o(c){var u=Ij(s(c),e,t);return i.concat(u)}for(;t.length=0;ge--){var re=B.definition[ge],O={idx:p,def:re.definition.concat((0,Kt.drop)(h)),ruleStack:C,occurrenceStack:y};g.push(O),g.push(o)}else if(B instanceof kt.Alternative)g.push({idx:p,def:B.definition.concat((0,Kt.drop)(h)),ruleStack:C,occurrenceStack:y});else if(B instanceof kt.Rule)g.push(UEe(B,p,C,y));else throw Error("non exhaustive match")}}return u}Nr.nextPossibleTokensAfter=KEe;function UEe(r,e,t,i){var n=(0,Kt.cloneArr)(t);n.push(r.name);var s=(0,Kt.cloneArr)(i);return s.push(1),{idx:e,def:r.definition,ruleStack:n,occurrenceStack:s}}});var Id=w(_t=>{"use strict";var Bj=_t&&_t.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(_t,"__esModule",{value:!0});_t.areTokenCategoriesNotUsed=_t.isStrictPrefixOfPath=_t.containsPath=_t.getLookaheadPathsForOptionalProd=_t.getLookaheadPathsForOr=_t.lookAheadSequenceFromAlternatives=_t.buildSingleAlternativeLookaheadFunction=_t.buildAlternativesLookAheadFunc=_t.buildLookaheadFuncForOptionalProd=_t.buildLookaheadFuncForOr=_t.getProdType=_t.PROD_TYPE=void 0;var sr=Gt(),yj=Ed(),HEe=VI(),ty=Gg(),xA=dn(),GEe=Yg(),oi;(function(r){r[r.OPTION=0]="OPTION",r[r.REPETITION=1]="REPETITION",r[r.REPETITION_MANDATORY=2]="REPETITION_MANDATORY",r[r.REPETITION_MANDATORY_WITH_SEPARATOR=3]="REPETITION_MANDATORY_WITH_SEPARATOR",r[r.REPETITION_WITH_SEPARATOR=4]="REPETITION_WITH_SEPARATOR",r[r.ALTERNATION=5]="ALTERNATION"})(oi=_t.PROD_TYPE||(_t.PROD_TYPE={}));function YEe(r){if(r instanceof xA.Option)return oi.OPTION;if(r instanceof xA.Repetition)return oi.REPETITION;if(r instanceof xA.RepetitionMandatory)return oi.REPETITION_MANDATORY;if(r instanceof xA.RepetitionMandatoryWithSeparator)return oi.REPETITION_MANDATORY_WITH_SEPARATOR;if(r instanceof xA.RepetitionWithSeparator)return oi.REPETITION_WITH_SEPARATOR;if(r instanceof xA.Alternation)return oi.ALTERNATION;throw Error("non exhaustive match")}_t.getProdType=YEe;function jEe(r,e,t,i,n,s){var o=bj(r,e,t),a=Lv(o)?ty.tokenStructuredMatcherNoCategories:ty.tokenStructuredMatcher;return s(o,i,a,n)}_t.buildLookaheadFuncForOr=jEe;function qEe(r,e,t,i,n,s){var o=Sj(r,e,n,t),a=Lv(o)?ty.tokenStructuredMatcherNoCategories:ty.tokenStructuredMatcher;return s(o[0],a,i)}_t.buildLookaheadFuncForOptionalProd=qEe;function JEe(r,e,t,i){var n=r.length,s=(0,sr.every)(r,function(l){return(0,sr.every)(l,function(c){return c.length===1})});if(e)return function(l){for(var c=(0,sr.map)(l,function(D){return D.GATE}),u=0;u{"use strict";var Tv=zt&&zt.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(zt,"__esModule",{value:!0});zt.checkPrefixAlternativesAmbiguities=zt.validateSomeNonEmptyLookaheadPath=zt.validateTooManyAlts=zt.RepetionCollector=zt.validateAmbiguousAlternationAlternatives=zt.validateEmptyOrAlternative=zt.getFirstNoneTerminal=zt.validateNoLeftRecursion=zt.validateRuleIsOverridden=zt.validateRuleDoesNotAlreadyExist=zt.OccurrenceValidationCollector=zt.identifyProductionForDuplicates=zt.validateGrammar=void 0;var er=Gt(),br=Gt(),Do=Hn(),Ov=dd(),qg=Id(),_Ee=Ed(),_s=dn(),Mv=Yg();function ZEe(r,e,t,i,n){var s=er.map(r,function(h){return $Ee(h,i)}),o=er.map(r,function(h){return Kv(h,h,i)}),a=[],l=[],c=[];(0,br.every)(o,br.isEmpty)&&(a=(0,br.map)(r,function(h){return Rj(h,i)}),l=(0,br.map)(r,function(h){return Fj(h,e,i)}),c=Tj(r,e,i));var u=rIe(r,t,i),g=(0,br.map)(r,function(h){return Lj(h,i)}),f=(0,br.map)(r,function(h){return kj(h,r,n,i)});return er.flatten(s.concat(c,o,a,l,u,g,f))}zt.validateGrammar=ZEe;function $Ee(r,e){var t=new Dj;r.accept(t);var i=t.allProductions,n=er.groupBy(i,xj),s=er.pick(n,function(a){return a.length>1}),o=er.map(er.values(s),function(a){var l=er.first(a),c=e.buildDuplicateFoundError(r,a),u=(0,Ov.getProductionDslName)(l),g={message:c,type:Do.ParserDefinitionErrorType.DUPLICATE_PRODUCTIONS,ruleName:r.name,dslName:u,occurrence:l.idx},f=Pj(l);return f&&(g.parameter=f),g});return o}function xj(r){return(0,Ov.getProductionDslName)(r)+"_#_"+r.idx+"_#_"+Pj(r)}zt.identifyProductionForDuplicates=xj;function Pj(r){return r instanceof _s.Terminal?r.terminalType.name:r instanceof _s.NonTerminal?r.nonTerminalName:""}var Dj=function(r){Tv(e,r);function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.allProductions=[],t}return e.prototype.visitNonTerminal=function(t){this.allProductions.push(t)},e.prototype.visitOption=function(t){this.allProductions.push(t)},e.prototype.visitRepetitionWithSeparator=function(t){this.allProductions.push(t)},e.prototype.visitRepetitionMandatory=function(t){this.allProductions.push(t)},e.prototype.visitRepetitionMandatoryWithSeparator=function(t){this.allProductions.push(t)},e.prototype.visitRepetition=function(t){this.allProductions.push(t)},e.prototype.visitAlternation=function(t){this.allProductions.push(t)},e.prototype.visitTerminal=function(t){this.allProductions.push(t)},e}(Mv.GAstVisitor);zt.OccurrenceValidationCollector=Dj;function kj(r,e,t,i){var n=[],s=(0,br.reduce)(e,function(a,l){return l.name===r.name?a+1:a},0);if(s>1){var o=i.buildDuplicateRuleNameError({topLevelRule:r,grammarName:t});n.push({message:o,type:Do.ParserDefinitionErrorType.DUPLICATE_RULE_NAME,ruleName:r.name})}return n}zt.validateRuleDoesNotAlreadyExist=kj;function eIe(r,e,t){var i=[],n;return er.contains(e,r)||(n="Invalid rule override, rule: ->"+r+"<- cannot be overridden in the grammar: ->"+t+"<-as it is not defined in any of the super grammars ",i.push({message:n,type:Do.ParserDefinitionErrorType.INVALID_RULE_OVERRIDE,ruleName:r})),i}zt.validateRuleIsOverridden=eIe;function Kv(r,e,t,i){i===void 0&&(i=[]);var n=[],s=yd(e.definition);if(er.isEmpty(s))return[];var o=r.name,a=er.contains(s,r);a&&n.push({message:t.buildLeftRecursionError({topLevelRule:r,leftRecursionPath:i}),type:Do.ParserDefinitionErrorType.LEFT_RECURSION,ruleName:o});var l=er.difference(s,i.concat([r])),c=er.map(l,function(u){var g=er.cloneArr(i);return g.push(u),Kv(r,u,t,g)});return n.concat(er.flatten(c))}zt.validateNoLeftRecursion=Kv;function yd(r){var e=[];if(er.isEmpty(r))return e;var t=er.first(r);if(t instanceof _s.NonTerminal)e.push(t.referencedRule);else if(t instanceof _s.Alternative||t instanceof _s.Option||t instanceof _s.RepetitionMandatory||t instanceof _s.RepetitionMandatoryWithSeparator||t instanceof _s.RepetitionWithSeparator||t instanceof _s.Repetition)e=e.concat(yd(t.definition));else if(t instanceof _s.Alternation)e=er.flatten(er.map(t.definition,function(o){return yd(o.definition)}));else if(!(t instanceof _s.Terminal))throw Error("non exhaustive match");var i=(0,Ov.isOptionalProd)(t),n=r.length>1;if(i&&n){var s=er.drop(r);return e.concat(yd(s))}else return e}zt.getFirstNoneTerminal=yd;var Uv=function(r){Tv(e,r);function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.alternations=[],t}return e.prototype.visitAlternation=function(t){this.alternations.push(t)},e}(Mv.GAstVisitor);function Rj(r,e){var t=new Uv;r.accept(t);var i=t.alternations,n=er.reduce(i,function(s,o){var a=er.dropRight(o.definition),l=er.map(a,function(c,u){var g=(0,_Ee.nextPossibleTokensAfter)([c],[],null,1);return er.isEmpty(g)?{message:e.buildEmptyAlternationError({topLevelRule:r,alternation:o,emptyChoiceIdx:u}),type:Do.ParserDefinitionErrorType.NONE_LAST_EMPTY_ALT,ruleName:r.name,occurrence:o.idx,alternative:u+1}:null});return s.concat(er.compact(l))},[]);return n}zt.validateEmptyOrAlternative=Rj;function Fj(r,e,t){var i=new Uv;r.accept(i);var n=i.alternations;n=(0,br.reject)(n,function(o){return o.ignoreAmbiguities===!0});var s=er.reduce(n,function(o,a){var l=a.idx,c=a.maxLookahead||e,u=(0,qg.getLookaheadPathsForOr)(l,r,c,a),g=tIe(u,a,r,t),f=Oj(u,a,r,t);return o.concat(g,f)},[]);return s}zt.validateAmbiguousAlternationAlternatives=Fj;var Nj=function(r){Tv(e,r);function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.allProductions=[],t}return e.prototype.visitRepetitionWithSeparator=function(t){this.allProductions.push(t)},e.prototype.visitRepetitionMandatory=function(t){this.allProductions.push(t)},e.prototype.visitRepetitionMandatoryWithSeparator=function(t){this.allProductions.push(t)},e.prototype.visitRepetition=function(t){this.allProductions.push(t)},e}(Mv.GAstVisitor);zt.RepetionCollector=Nj;function Lj(r,e){var t=new Uv;r.accept(t);var i=t.alternations,n=er.reduce(i,function(s,o){return o.definition.length>255&&s.push({message:e.buildTooManyAlternativesError({topLevelRule:r,alternation:o}),type:Do.ParserDefinitionErrorType.TOO_MANY_ALTS,ruleName:r.name,occurrence:o.idx}),s},[]);return n}zt.validateTooManyAlts=Lj;function Tj(r,e,t){var i=[];return(0,br.forEach)(r,function(n){var s=new Nj;n.accept(s);var o=s.allProductions;(0,br.forEach)(o,function(a){var l=(0,qg.getProdType)(a),c=a.maxLookahead||e,u=a.idx,g=(0,qg.getLookaheadPathsForOptionalProd)(u,n,l,c),f=g[0];if((0,br.isEmpty)((0,br.flatten)(f))){var h=t.buildEmptyRepetitionError({topLevelRule:n,repetition:a});i.push({message:h,type:Do.ParserDefinitionErrorType.NO_NON_EMPTY_LOOKAHEAD,ruleName:n.name})}})}),i}zt.validateSomeNonEmptyLookaheadPath=Tj;function tIe(r,e,t,i){var n=[],s=(0,br.reduce)(r,function(a,l,c){return e.definition[c].ignoreAmbiguities===!0||(0,br.forEach)(l,function(u){var g=[c];(0,br.forEach)(r,function(f,h){c!==h&&(0,qg.containsPath)(f,u)&&e.definition[h].ignoreAmbiguities!==!0&&g.push(h)}),g.length>1&&!(0,qg.containsPath)(n,u)&&(n.push(u),a.push({alts:g,path:u}))}),a},[]),o=er.map(s,function(a){var l=(0,br.map)(a.alts,function(u){return u+1}),c=i.buildAlternationAmbiguityError({topLevelRule:t,alternation:e,ambiguityIndices:l,prefixPath:a.path});return{message:c,type:Do.ParserDefinitionErrorType.AMBIGUOUS_ALTS,ruleName:t.name,occurrence:e.idx,alternatives:[a.alts]}});return o}function Oj(r,e,t,i){var n=[],s=(0,br.reduce)(r,function(o,a,l){var c=(0,br.map)(a,function(u){return{idx:l,path:u}});return o.concat(c)},[]);return(0,br.forEach)(s,function(o){var a=e.definition[o.idx];if(a.ignoreAmbiguities!==!0){var l=o.idx,c=o.path,u=(0,br.findAll)(s,function(f){return e.definition[f.idx].ignoreAmbiguities!==!0&&f.idx{"use strict";Object.defineProperty(Jg,"__esModule",{value:!0});Jg.validateGrammar=Jg.resolveGrammar=void 0;var Gv=Gt(),iIe=Cj(),nIe=Hv(),Mj=Cd();function sIe(r){r=(0,Gv.defaults)(r,{errMsgProvider:Mj.defaultGrammarResolverErrorProvider});var e={};return(0,Gv.forEach)(r.rules,function(t){e[t.name]=t}),(0,iIe.resolveGrammar)(e,r.errMsgProvider)}Jg.resolveGrammar=sIe;function oIe(r){return r=(0,Gv.defaults)(r,{errMsgProvider:Mj.defaultGrammarValidatorErrorProvider}),(0,nIe.validateGrammar)(r.rules,r.maxLookahead,r.tokenTypes,r.errMsgProvider,r.grammarName)}Jg.validateGrammar=oIe});var Wg=w(mn=>{"use strict";var wd=mn&&mn.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(mn,"__esModule",{value:!0});mn.EarlyExitException=mn.NotAllInputParsedException=mn.NoViableAltException=mn.MismatchedTokenException=mn.isRecognitionException=void 0;var aIe=Gt(),Uj="MismatchedTokenException",Hj="NoViableAltException",Gj="EarlyExitException",Yj="NotAllInputParsedException",jj=[Uj,Hj,Gj,Yj];Object.freeze(jj);function AIe(r){return(0,aIe.contains)(jj,r.name)}mn.isRecognitionException=AIe;var ry=function(r){wd(e,r);function e(t,i){var n=this.constructor,s=r.call(this,t)||this;return s.token=i,s.resyncedTokens=[],Object.setPrototypeOf(s,n.prototype),Error.captureStackTrace&&Error.captureStackTrace(s,s.constructor),s}return e}(Error),lIe=function(r){wd(e,r);function e(t,i,n){var s=r.call(this,t,i)||this;return s.previousToken=n,s.name=Uj,s}return e}(ry);mn.MismatchedTokenException=lIe;var cIe=function(r){wd(e,r);function e(t,i,n){var s=r.call(this,t,i)||this;return s.previousToken=n,s.name=Hj,s}return e}(ry);mn.NoViableAltException=cIe;var uIe=function(r){wd(e,r);function e(t,i){var n=r.call(this,t,i)||this;return n.name=Yj,n}return e}(ry);mn.NotAllInputParsedException=uIe;var gIe=function(r){wd(e,r);function e(t,i,n){var s=r.call(this,t,i)||this;return s.previousToken=n,s.name=Gj,s}return e}(ry);mn.EarlyExitException=gIe});var jv=w(Mi=>{"use strict";Object.defineProperty(Mi,"__esModule",{value:!0});Mi.attemptInRepetitionRecovery=Mi.Recoverable=Mi.InRuleRecoveryException=Mi.IN_RULE_RECOVERY_EXCEPTION=Mi.EOF_FOLLOW_KEY=void 0;var iy=SA(),ls=Gt(),fIe=Wg(),hIe=kv(),pIe=Hn();Mi.EOF_FOLLOW_KEY={};Mi.IN_RULE_RECOVERY_EXCEPTION="InRuleRecoveryException";function Yv(r){this.name=Mi.IN_RULE_RECOVERY_EXCEPTION,this.message=r}Mi.InRuleRecoveryException=Yv;Yv.prototype=Error.prototype;var dIe=function(){function r(){}return r.prototype.initRecoverable=function(e){this.firstAfterRepMap={},this.resyncFollows={},this.recoveryEnabled=(0,ls.has)(e,"recoveryEnabled")?e.recoveryEnabled:pIe.DEFAULT_PARSER_CONFIG.recoveryEnabled,this.recoveryEnabled&&(this.attemptInRepetitionRecovery=qj)},r.prototype.getTokenToInsert=function(e){var t=(0,iy.createTokenInstance)(e,"",NaN,NaN,NaN,NaN,NaN,NaN);return t.isInsertedInRecovery=!0,t},r.prototype.canTokenTypeBeInsertedInRecovery=function(e){return!0},r.prototype.tryInRepetitionRecovery=function(e,t,i,n){for(var s=this,o=this.findReSyncTokenType(),a=this.exportLexerState(),l=[],c=!1,u=this.LA(1),g=this.LA(1),f=function(){var h=s.LA(0),p=s.errorMessageProvider.buildMismatchTokenMessage({expected:n,actual:u,previous:h,ruleName:s.getCurrRuleFullName()}),C=new fIe.MismatchedTokenException(p,u,s.LA(0));C.resyncedTokens=(0,ls.dropRight)(l),s.SAVE_ERROR(C)};!c;)if(this.tokenMatcher(g,n)){f();return}else if(i.call(this)){f(),e.apply(this,t);return}else this.tokenMatcher(g,o)?c=!0:(g=this.SKIP_TOKEN(),this.addToResyncTokens(g,l));this.importLexerState(a)},r.prototype.shouldInRepetitionRecoveryBeTried=function(e,t,i){return!(i===!1||e===void 0||t===void 0||this.tokenMatcher(this.LA(1),e)||this.isBackTracking()||this.canPerformInRuleRecovery(e,this.getFollowsForInRuleRecovery(e,t)))},r.prototype.getFollowsForInRuleRecovery=function(e,t){var i=this.getCurrentGrammarPath(e,t),n=this.getNextPossibleTokenTypes(i);return n},r.prototype.tryInRuleRecovery=function(e,t){if(this.canRecoverWithSingleTokenInsertion(e,t)){var i=this.getTokenToInsert(e);return i}if(this.canRecoverWithSingleTokenDeletion(e)){var n=this.SKIP_TOKEN();return this.consumeToken(),n}throw new Yv("sad sad panda")},r.prototype.canPerformInRuleRecovery=function(e,t){return this.canRecoverWithSingleTokenInsertion(e,t)||this.canRecoverWithSingleTokenDeletion(e)},r.prototype.canRecoverWithSingleTokenInsertion=function(e,t){var i=this;if(!this.canTokenTypeBeInsertedInRecovery(e)||(0,ls.isEmpty)(t))return!1;var n=this.LA(1),s=(0,ls.find)(t,function(o){return i.tokenMatcher(n,o)})!==void 0;return s},r.prototype.canRecoverWithSingleTokenDeletion=function(e){var t=this.tokenMatcher(this.LA(2),e);return t},r.prototype.isInCurrentRuleReSyncSet=function(e){var t=this.getCurrFollowKey(),i=this.getFollowSetFromFollowKey(t);return(0,ls.contains)(i,e)},r.prototype.findReSyncTokenType=function(){for(var e=this.flattenFollowSet(),t=this.LA(1),i=2;;){var n=t.tokenType;if((0,ls.contains)(e,n))return n;t=this.LA(i),i++}},r.prototype.getCurrFollowKey=function(){if(this.RULE_STACK.length===1)return Mi.EOF_FOLLOW_KEY;var e=this.getLastExplicitRuleShortName(),t=this.getLastExplicitRuleOccurrenceIndex(),i=this.getPreviousExplicitRuleShortName();return{ruleName:this.shortRuleNameToFullName(e),idxInCallingRule:t,inRule:this.shortRuleNameToFullName(i)}},r.prototype.buildFullFollowKeyStack=function(){var e=this,t=this.RULE_STACK,i=this.RULE_OCCURRENCE_STACK;return(0,ls.map)(t,function(n,s){return s===0?Mi.EOF_FOLLOW_KEY:{ruleName:e.shortRuleNameToFullName(n),idxInCallingRule:i[s],inRule:e.shortRuleNameToFullName(t[s-1])}})},r.prototype.flattenFollowSet=function(){var e=this,t=(0,ls.map)(this.buildFullFollowKeyStack(),function(i){return e.getFollowSetFromFollowKey(i)});return(0,ls.flatten)(t)},r.prototype.getFollowSetFromFollowKey=function(e){if(e===Mi.EOF_FOLLOW_KEY)return[iy.EOF];var t=e.ruleName+e.idxInCallingRule+hIe.IN+e.inRule;return this.resyncFollows[t]},r.prototype.addToResyncTokens=function(e,t){return this.tokenMatcher(e,iy.EOF)||t.push(e),t},r.prototype.reSyncTo=function(e){for(var t=[],i=this.LA(1);this.tokenMatcher(i,e)===!1;)i=this.SKIP_TOKEN(),this.addToResyncTokens(i,t);return(0,ls.dropRight)(t)},r.prototype.attemptInRepetitionRecovery=function(e,t,i,n,s,o,a){},r.prototype.getCurrentGrammarPath=function(e,t){var i=this.getHumanReadableRuleStack(),n=(0,ls.cloneArr)(this.RULE_OCCURRENCE_STACK),s={ruleStack:i,occurrenceStack:n,lastTok:e,lastTokOccurrence:t};return s},r.prototype.getHumanReadableRuleStack=function(){var e=this;return(0,ls.map)(this.RULE_STACK,function(t){return e.shortRuleNameToFullName(t)})},r}();Mi.Recoverable=dIe;function qj(r,e,t,i,n,s,o){var a=this.getKeyForAutomaticLookahead(i,n),l=this.firstAfterRepMap[a];if(l===void 0){var c=this.getCurrRuleFullName(),u=this.getGAstProductions()[c],g=new s(u,n);l=g.startWalking(),this.firstAfterRepMap[a]=l}var f=l.token,h=l.occurrence,p=l.isEndOfRule;this.RULE_STACK.length===1&&p&&f===void 0&&(f=iy.EOF,h=1),this.shouldInRepetitionRecoveryBeTried(f,h,o)&&this.tryInRepetitionRecovery(r,e,t,f)}Mi.attemptInRepetitionRecovery=qj});var ny=w(qt=>{"use strict";Object.defineProperty(qt,"__esModule",{value:!0});qt.getKeyForAutomaticLookahead=qt.AT_LEAST_ONE_SEP_IDX=qt.MANY_SEP_IDX=qt.AT_LEAST_ONE_IDX=qt.MANY_IDX=qt.OPTION_IDX=qt.OR_IDX=qt.BITS_FOR_ALT_IDX=qt.BITS_FOR_RULE_IDX=qt.BITS_FOR_OCCURRENCE_IDX=qt.BITS_FOR_METHOD_TYPE=void 0;qt.BITS_FOR_METHOD_TYPE=4;qt.BITS_FOR_OCCURRENCE_IDX=8;qt.BITS_FOR_RULE_IDX=12;qt.BITS_FOR_ALT_IDX=8;qt.OR_IDX=1<{"use strict";Object.defineProperty(sy,"__esModule",{value:!0});sy.LooksAhead=void 0;var Qa=Id(),Zs=Gt(),Jj=Hn(),ba=ny(),oc=dd(),mIe=function(){function r(){}return r.prototype.initLooksAhead=function(e){this.dynamicTokensEnabled=(0,Zs.has)(e,"dynamicTokensEnabled")?e.dynamicTokensEnabled:Jj.DEFAULT_PARSER_CONFIG.dynamicTokensEnabled,this.maxLookahead=(0,Zs.has)(e,"maxLookahead")?e.maxLookahead:Jj.DEFAULT_PARSER_CONFIG.maxLookahead,this.lookAheadFuncsCache=(0,Zs.isES2015MapSupported)()?new Map:[],(0,Zs.isES2015MapSupported)()?(this.getLaFuncFromCache=this.getLaFuncFromMap,this.setLaFuncCache=this.setLaFuncCacheUsingMap):(this.getLaFuncFromCache=this.getLaFuncFromObj,this.setLaFuncCache=this.setLaFuncUsingObj)},r.prototype.preComputeLookaheadFunctions=function(e){var t=this;(0,Zs.forEach)(e,function(i){t.TRACE_INIT(i.name+" Rule Lookahead",function(){var n=(0,oc.collectMethods)(i),s=n.alternation,o=n.repetition,a=n.option,l=n.repetitionMandatory,c=n.repetitionMandatoryWithSeparator,u=n.repetitionWithSeparator;(0,Zs.forEach)(s,function(g){var f=g.idx===0?"":g.idx;t.TRACE_INIT(""+(0,oc.getProductionDslName)(g)+f,function(){var h=(0,Qa.buildLookaheadFuncForOr)(g.idx,i,g.maxLookahead||t.maxLookahead,g.hasPredicates,t.dynamicTokensEnabled,t.lookAheadBuilderForAlternatives),p=(0,ba.getKeyForAutomaticLookahead)(t.fullRuleNameToShort[i.name],ba.OR_IDX,g.idx);t.setLaFuncCache(p,h)})}),(0,Zs.forEach)(o,function(g){t.computeLookaheadFunc(i,g.idx,ba.MANY_IDX,Qa.PROD_TYPE.REPETITION,g.maxLookahead,(0,oc.getProductionDslName)(g))}),(0,Zs.forEach)(a,function(g){t.computeLookaheadFunc(i,g.idx,ba.OPTION_IDX,Qa.PROD_TYPE.OPTION,g.maxLookahead,(0,oc.getProductionDslName)(g))}),(0,Zs.forEach)(l,function(g){t.computeLookaheadFunc(i,g.idx,ba.AT_LEAST_ONE_IDX,Qa.PROD_TYPE.REPETITION_MANDATORY,g.maxLookahead,(0,oc.getProductionDslName)(g))}),(0,Zs.forEach)(c,function(g){t.computeLookaheadFunc(i,g.idx,ba.AT_LEAST_ONE_SEP_IDX,Qa.PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR,g.maxLookahead,(0,oc.getProductionDslName)(g))}),(0,Zs.forEach)(u,function(g){t.computeLookaheadFunc(i,g.idx,ba.MANY_SEP_IDX,Qa.PROD_TYPE.REPETITION_WITH_SEPARATOR,g.maxLookahead,(0,oc.getProductionDslName)(g))})})})},r.prototype.computeLookaheadFunc=function(e,t,i,n,s,o){var a=this;this.TRACE_INIT(""+o+(t===0?"":t),function(){var l=(0,Qa.buildLookaheadFuncForOptionalProd)(t,e,s||a.maxLookahead,a.dynamicTokensEnabled,n,a.lookAheadBuilderForOptional),c=(0,ba.getKeyForAutomaticLookahead)(a.fullRuleNameToShort[e.name],i,t);a.setLaFuncCache(c,l)})},r.prototype.lookAheadBuilderForOptional=function(e,t,i){return(0,Qa.buildSingleAlternativeLookaheadFunction)(e,t,i)},r.prototype.lookAheadBuilderForAlternatives=function(e,t,i,n){return(0,Qa.buildAlternativesLookAheadFunc)(e,t,i,n)},r.prototype.getKeyForAutomaticLookahead=function(e,t){var i=this.getLastExplicitRuleShortName();return(0,ba.getKeyForAutomaticLookahead)(i,e,t)},r.prototype.getLaFuncFromCache=function(e){},r.prototype.getLaFuncFromMap=function(e){return this.lookAheadFuncsCache.get(e)},r.prototype.getLaFuncFromObj=function(e){return this.lookAheadFuncsCache[e]},r.prototype.setLaFuncCache=function(e,t){},r.prototype.setLaFuncCacheUsingMap=function(e,t){this.lookAheadFuncsCache.set(e,t)},r.prototype.setLaFuncUsingObj=function(e,t){this.lookAheadFuncsCache[e]=t},r}();sy.LooksAhead=mIe});var zj=w(ko=>{"use strict";Object.defineProperty(ko,"__esModule",{value:!0});ko.addNoneTerminalToCst=ko.addTerminalToCst=ko.setNodeLocationFull=ko.setNodeLocationOnlyOffset=void 0;function EIe(r,e){isNaN(r.startOffset)===!0?(r.startOffset=e.startOffset,r.endOffset=e.endOffset):r.endOffset{"use strict";Object.defineProperty(PA,"__esModule",{value:!0});PA.defineNameProp=PA.functionName=PA.classNameFromInstance=void 0;var BIe=Gt();function QIe(r){return Xj(r.constructor)}PA.classNameFromInstance=QIe;var Vj="name";function Xj(r){var e=r.name;return e||"anonymous"}PA.functionName=Xj;function bIe(r,e){var t=Object.getOwnPropertyDescriptor(r,Vj);return(0,BIe.isUndefined)(t)||t.configurable?(Object.defineProperty(r,Vj,{enumerable:!1,configurable:!0,writable:!1,value:e}),!0):!1}PA.defineNameProp=bIe});var tq=w(bi=>{"use strict";Object.defineProperty(bi,"__esModule",{value:!0});bi.validateRedundantMethods=bi.validateMissingCstMethods=bi.validateVisitor=bi.CstVisitorDefinitionError=bi.createBaseVisitorConstructorWithDefaults=bi.createBaseSemanticVisitorConstructor=bi.defaultVisit=void 0;var cs=Gt(),Bd=qv();function _j(r,e){for(var t=(0,cs.keys)(r),i=t.length,n=0;n: + `+(""+s.join(` + +`).replace(/\n/g,` + `)))}}};return t.prototype=i,t.prototype.constructor=t,t._RULE_NAMES=e,t}bi.createBaseSemanticVisitorConstructor=SIe;function vIe(r,e,t){var i=function(){};(0,Bd.defineNameProp)(i,r+"BaseSemanticsWithDefaults");var n=Object.create(t.prototype);return(0,cs.forEach)(e,function(s){n[s]=_j}),i.prototype=n,i.prototype.constructor=i,i}bi.createBaseVisitorConstructorWithDefaults=vIe;var Jv;(function(r){r[r.REDUNDANT_METHOD=0]="REDUNDANT_METHOD",r[r.MISSING_METHOD=1]="MISSING_METHOD"})(Jv=bi.CstVisitorDefinitionError||(bi.CstVisitorDefinitionError={}));function Zj(r,e){var t=$j(r,e),i=eq(r,e);return t.concat(i)}bi.validateVisitor=Zj;function $j(r,e){var t=(0,cs.map)(e,function(i){if(!(0,cs.isFunction)(r[i]))return{msg:"Missing visitor method: <"+i+"> on "+(0,Bd.functionName)(r.constructor)+" CST Visitor.",type:Jv.MISSING_METHOD,methodName:i}});return(0,cs.compact)(t)}bi.validateMissingCstMethods=$j;var xIe=["constructor","visit","validateVisitor"];function eq(r,e){var t=[];for(var i in r)(0,cs.isFunction)(r[i])&&!(0,cs.contains)(xIe,i)&&!(0,cs.contains)(e,i)&&t.push({msg:"Redundant visitor method: <"+i+"> on "+(0,Bd.functionName)(r.constructor)+` CST Visitor +There is no Grammar Rule corresponding to this method's name. +`,type:Jv.REDUNDANT_METHOD,methodName:i});return t}bi.validateRedundantMethods=eq});var iq=w(oy=>{"use strict";Object.defineProperty(oy,"__esModule",{value:!0});oy.TreeBuilder=void 0;var zg=zj(),Zr=Gt(),rq=tq(),PIe=Hn(),DIe=function(){function r(){}return r.prototype.initTreeBuilder=function(e){if(this.CST_STACK=[],this.outputCst=e.outputCst,this.nodeLocationTracking=(0,Zr.has)(e,"nodeLocationTracking")?e.nodeLocationTracking:PIe.DEFAULT_PARSER_CONFIG.nodeLocationTracking,!this.outputCst)this.cstInvocationStateUpdate=Zr.NOOP,this.cstFinallyStateUpdate=Zr.NOOP,this.cstPostTerminal=Zr.NOOP,this.cstPostNonTerminal=Zr.NOOP,this.cstPostRule=Zr.NOOP;else if(/full/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=zg.setNodeLocationFull,this.setNodeLocationFromNode=zg.setNodeLocationFull,this.cstPostRule=Zr.NOOP,this.setInitialNodeLocation=this.setInitialNodeLocationFullRecovery):(this.setNodeLocationFromToken=Zr.NOOP,this.setNodeLocationFromNode=Zr.NOOP,this.cstPostRule=this.cstPostRuleFull,this.setInitialNodeLocation=this.setInitialNodeLocationFullRegular);else if(/onlyOffset/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=zg.setNodeLocationOnlyOffset,this.setNodeLocationFromNode=zg.setNodeLocationOnlyOffset,this.cstPostRule=Zr.NOOP,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRecovery):(this.setNodeLocationFromToken=Zr.NOOP,this.setNodeLocationFromNode=Zr.NOOP,this.cstPostRule=this.cstPostRuleOnlyOffset,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRegular);else if(/none/i.test(this.nodeLocationTracking))this.setNodeLocationFromToken=Zr.NOOP,this.setNodeLocationFromNode=Zr.NOOP,this.cstPostRule=Zr.NOOP,this.setInitialNodeLocation=Zr.NOOP;else throw Error('Invalid config option: "'+e.nodeLocationTracking+'"')},r.prototype.setInitialNodeLocationOnlyOffsetRecovery=function(e){e.location={startOffset:NaN,endOffset:NaN}},r.prototype.setInitialNodeLocationOnlyOffsetRegular=function(e){e.location={startOffset:this.LA(1).startOffset,endOffset:NaN}},r.prototype.setInitialNodeLocationFullRecovery=function(e){e.location={startOffset:NaN,startLine:NaN,startColumn:NaN,endOffset:NaN,endLine:NaN,endColumn:NaN}},r.prototype.setInitialNodeLocationFullRegular=function(e){var t=this.LA(1);e.location={startOffset:t.startOffset,startLine:t.startLine,startColumn:t.startColumn,endOffset:NaN,endLine:NaN,endColumn:NaN}},r.prototype.cstInvocationStateUpdate=function(e,t){var i={name:e,children:{}};this.setInitialNodeLocation(i),this.CST_STACK.push(i)},r.prototype.cstFinallyStateUpdate=function(){this.CST_STACK.pop()},r.prototype.cstPostRuleFull=function(e){var t=this.LA(0),i=e.location;i.startOffset<=t.startOffset?(i.endOffset=t.endOffset,i.endLine=t.endLine,i.endColumn=t.endColumn):(i.startOffset=NaN,i.startLine=NaN,i.startColumn=NaN)},r.prototype.cstPostRuleOnlyOffset=function(e){var t=this.LA(0),i=e.location;i.startOffset<=t.startOffset?i.endOffset=t.endOffset:i.startOffset=NaN},r.prototype.cstPostTerminal=function(e,t){var i=this.CST_STACK[this.CST_STACK.length-1];(0,zg.addTerminalToCst)(i,t,e),this.setNodeLocationFromToken(i.location,t)},r.prototype.cstPostNonTerminal=function(e,t){var i=this.CST_STACK[this.CST_STACK.length-1];(0,zg.addNoneTerminalToCst)(i,t,e),this.setNodeLocationFromNode(i.location,e.location)},r.prototype.getBaseCstVisitorConstructor=function(){if((0,Zr.isUndefined)(this.baseCstVisitorConstructor)){var e=(0,rq.createBaseSemanticVisitorConstructor)(this.className,(0,Zr.keys)(this.gastProductionsCache));return this.baseCstVisitorConstructor=e,e}return this.baseCstVisitorConstructor},r.prototype.getBaseCstVisitorConstructorWithDefaults=function(){if((0,Zr.isUndefined)(this.baseCstVisitorWithDefaultsConstructor)){var e=(0,rq.createBaseVisitorConstructorWithDefaults)(this.className,(0,Zr.keys)(this.gastProductionsCache),this.getBaseCstVisitorConstructor());return this.baseCstVisitorWithDefaultsConstructor=e,e}return this.baseCstVisitorWithDefaultsConstructor},r.prototype.getLastExplicitRuleShortName=function(){var e=this.RULE_STACK;return e[e.length-1]},r.prototype.getPreviousExplicitRuleShortName=function(){var e=this.RULE_STACK;return e[e.length-2]},r.prototype.getLastExplicitRuleOccurrenceIndex=function(){var e=this.RULE_OCCURRENCE_STACK;return e[e.length-1]},r}();oy.TreeBuilder=DIe});var sq=w(ay=>{"use strict";Object.defineProperty(ay,"__esModule",{value:!0});ay.LexerAdapter=void 0;var nq=Hn(),kIe=function(){function r(){}return r.prototype.initLexerAdapter=function(){this.tokVector=[],this.tokVectorLength=0,this.currIdx=-1},Object.defineProperty(r.prototype,"input",{get:function(){return this.tokVector},set:function(e){if(this.selfAnalysisDone!==!0)throw Error("Missing invocation at the end of the Parser's constructor.");this.reset(),this.tokVector=e,this.tokVectorLength=e.length},enumerable:!1,configurable:!0}),r.prototype.SKIP_TOKEN=function(){return this.currIdx<=this.tokVector.length-2?(this.consumeToken(),this.LA(1)):nq.END_OF_FILE},r.prototype.LA=function(e){var t=this.currIdx+e;return t<0||this.tokVectorLength<=t?nq.END_OF_FILE:this.tokVector[t]},r.prototype.consumeToken=function(){this.currIdx++},r.prototype.exportLexerState=function(){return this.currIdx},r.prototype.importLexerState=function(e){this.currIdx=e},r.prototype.resetLexerState=function(){this.currIdx=-1},r.prototype.moveToTerminatedState=function(){this.currIdx=this.tokVector.length-1},r.prototype.getLexerPosition=function(){return this.exportLexerState()},r}();ay.LexerAdapter=kIe});var aq=w(Ay=>{"use strict";Object.defineProperty(Ay,"__esModule",{value:!0});Ay.RecognizerApi=void 0;var oq=Gt(),RIe=Wg(),Wv=Hn(),FIe=Cd(),NIe=Hv(),LIe=dn(),TIe=function(){function r(){}return r.prototype.ACTION=function(e){return e.call(this)},r.prototype.consume=function(e,t,i){return this.consumeInternal(t,e,i)},r.prototype.subrule=function(e,t,i){return this.subruleInternal(t,e,i)},r.prototype.option=function(e,t){return this.optionInternal(t,e)},r.prototype.or=function(e,t){return this.orInternal(t,e)},r.prototype.many=function(e,t){return this.manyInternal(e,t)},r.prototype.atLeastOne=function(e,t){return this.atLeastOneInternal(e,t)},r.prototype.CONSUME=function(e,t){return this.consumeInternal(e,0,t)},r.prototype.CONSUME1=function(e,t){return this.consumeInternal(e,1,t)},r.prototype.CONSUME2=function(e,t){return this.consumeInternal(e,2,t)},r.prototype.CONSUME3=function(e,t){return this.consumeInternal(e,3,t)},r.prototype.CONSUME4=function(e,t){return this.consumeInternal(e,4,t)},r.prototype.CONSUME5=function(e,t){return this.consumeInternal(e,5,t)},r.prototype.CONSUME6=function(e,t){return this.consumeInternal(e,6,t)},r.prototype.CONSUME7=function(e,t){return this.consumeInternal(e,7,t)},r.prototype.CONSUME8=function(e,t){return this.consumeInternal(e,8,t)},r.prototype.CONSUME9=function(e,t){return this.consumeInternal(e,9,t)},r.prototype.SUBRULE=function(e,t){return this.subruleInternal(e,0,t)},r.prototype.SUBRULE1=function(e,t){return this.subruleInternal(e,1,t)},r.prototype.SUBRULE2=function(e,t){return this.subruleInternal(e,2,t)},r.prototype.SUBRULE3=function(e,t){return this.subruleInternal(e,3,t)},r.prototype.SUBRULE4=function(e,t){return this.subruleInternal(e,4,t)},r.prototype.SUBRULE5=function(e,t){return this.subruleInternal(e,5,t)},r.prototype.SUBRULE6=function(e,t){return this.subruleInternal(e,6,t)},r.prototype.SUBRULE7=function(e,t){return this.subruleInternal(e,7,t)},r.prototype.SUBRULE8=function(e,t){return this.subruleInternal(e,8,t)},r.prototype.SUBRULE9=function(e,t){return this.subruleInternal(e,9,t)},r.prototype.OPTION=function(e){return this.optionInternal(e,0)},r.prototype.OPTION1=function(e){return this.optionInternal(e,1)},r.prototype.OPTION2=function(e){return this.optionInternal(e,2)},r.prototype.OPTION3=function(e){return this.optionInternal(e,3)},r.prototype.OPTION4=function(e){return this.optionInternal(e,4)},r.prototype.OPTION5=function(e){return this.optionInternal(e,5)},r.prototype.OPTION6=function(e){return this.optionInternal(e,6)},r.prototype.OPTION7=function(e){return this.optionInternal(e,7)},r.prototype.OPTION8=function(e){return this.optionInternal(e,8)},r.prototype.OPTION9=function(e){return this.optionInternal(e,9)},r.prototype.OR=function(e){return this.orInternal(e,0)},r.prototype.OR1=function(e){return this.orInternal(e,1)},r.prototype.OR2=function(e){return this.orInternal(e,2)},r.prototype.OR3=function(e){return this.orInternal(e,3)},r.prototype.OR4=function(e){return this.orInternal(e,4)},r.prototype.OR5=function(e){return this.orInternal(e,5)},r.prototype.OR6=function(e){return this.orInternal(e,6)},r.prototype.OR7=function(e){return this.orInternal(e,7)},r.prototype.OR8=function(e){return this.orInternal(e,8)},r.prototype.OR9=function(e){return this.orInternal(e,9)},r.prototype.MANY=function(e){this.manyInternal(0,e)},r.prototype.MANY1=function(e){this.manyInternal(1,e)},r.prototype.MANY2=function(e){this.manyInternal(2,e)},r.prototype.MANY3=function(e){this.manyInternal(3,e)},r.prototype.MANY4=function(e){this.manyInternal(4,e)},r.prototype.MANY5=function(e){this.manyInternal(5,e)},r.prototype.MANY6=function(e){this.manyInternal(6,e)},r.prototype.MANY7=function(e){this.manyInternal(7,e)},r.prototype.MANY8=function(e){this.manyInternal(8,e)},r.prototype.MANY9=function(e){this.manyInternal(9,e)},r.prototype.MANY_SEP=function(e){this.manySepFirstInternal(0,e)},r.prototype.MANY_SEP1=function(e){this.manySepFirstInternal(1,e)},r.prototype.MANY_SEP2=function(e){this.manySepFirstInternal(2,e)},r.prototype.MANY_SEP3=function(e){this.manySepFirstInternal(3,e)},r.prototype.MANY_SEP4=function(e){this.manySepFirstInternal(4,e)},r.prototype.MANY_SEP5=function(e){this.manySepFirstInternal(5,e)},r.prototype.MANY_SEP6=function(e){this.manySepFirstInternal(6,e)},r.prototype.MANY_SEP7=function(e){this.manySepFirstInternal(7,e)},r.prototype.MANY_SEP8=function(e){this.manySepFirstInternal(8,e)},r.prototype.MANY_SEP9=function(e){this.manySepFirstInternal(9,e)},r.prototype.AT_LEAST_ONE=function(e){this.atLeastOneInternal(0,e)},r.prototype.AT_LEAST_ONE1=function(e){return this.atLeastOneInternal(1,e)},r.prototype.AT_LEAST_ONE2=function(e){this.atLeastOneInternal(2,e)},r.prototype.AT_LEAST_ONE3=function(e){this.atLeastOneInternal(3,e)},r.prototype.AT_LEAST_ONE4=function(e){this.atLeastOneInternal(4,e)},r.prototype.AT_LEAST_ONE5=function(e){this.atLeastOneInternal(5,e)},r.prototype.AT_LEAST_ONE6=function(e){this.atLeastOneInternal(6,e)},r.prototype.AT_LEAST_ONE7=function(e){this.atLeastOneInternal(7,e)},r.prototype.AT_LEAST_ONE8=function(e){this.atLeastOneInternal(8,e)},r.prototype.AT_LEAST_ONE9=function(e){this.atLeastOneInternal(9,e)},r.prototype.AT_LEAST_ONE_SEP=function(e){this.atLeastOneSepFirstInternal(0,e)},r.prototype.AT_LEAST_ONE_SEP1=function(e){this.atLeastOneSepFirstInternal(1,e)},r.prototype.AT_LEAST_ONE_SEP2=function(e){this.atLeastOneSepFirstInternal(2,e)},r.prototype.AT_LEAST_ONE_SEP3=function(e){this.atLeastOneSepFirstInternal(3,e)},r.prototype.AT_LEAST_ONE_SEP4=function(e){this.atLeastOneSepFirstInternal(4,e)},r.prototype.AT_LEAST_ONE_SEP5=function(e){this.atLeastOneSepFirstInternal(5,e)},r.prototype.AT_LEAST_ONE_SEP6=function(e){this.atLeastOneSepFirstInternal(6,e)},r.prototype.AT_LEAST_ONE_SEP7=function(e){this.atLeastOneSepFirstInternal(7,e)},r.prototype.AT_LEAST_ONE_SEP8=function(e){this.atLeastOneSepFirstInternal(8,e)},r.prototype.AT_LEAST_ONE_SEP9=function(e){this.atLeastOneSepFirstInternal(9,e)},r.prototype.RULE=function(e,t,i){if(i===void 0&&(i=Wv.DEFAULT_RULE_CONFIG),(0,oq.contains)(this.definedRulesNames,e)){var n=FIe.defaultGrammarValidatorErrorProvider.buildDuplicateRuleNameError({topLevelRule:e,grammarName:this.className}),s={message:n,type:Wv.ParserDefinitionErrorType.DUPLICATE_RULE_NAME,ruleName:e};this.definitionErrors.push(s)}this.definedRulesNames.push(e);var o=this.defineRule(e,t,i);return this[e]=o,o},r.prototype.OVERRIDE_RULE=function(e,t,i){i===void 0&&(i=Wv.DEFAULT_RULE_CONFIG);var n=[];n=n.concat((0,NIe.validateRuleIsOverridden)(e,this.definedRulesNames,this.className)),this.definitionErrors=this.definitionErrors.concat(n);var s=this.defineRule(e,t,i);return this[e]=s,s},r.prototype.BACKTRACK=function(e,t){return function(){this.isBackTrackingStack.push(1);var i=this.saveRecogState();try{return e.apply(this,t),!0}catch(n){if((0,RIe.isRecognitionException)(n))return!1;throw n}finally{this.reloadRecogState(i),this.isBackTrackingStack.pop()}}},r.prototype.getGAstProductions=function(){return this.gastProductionsCache},r.prototype.getSerializedGastProductions=function(){return(0,LIe.serializeGrammar)((0,oq.values)(this.gastProductionsCache))},r}();Ay.RecognizerApi=TIe});var uq=w(cy=>{"use strict";Object.defineProperty(cy,"__esModule",{value:!0});cy.RecognizerEngine=void 0;var Pr=Gt(),Gn=ny(),ly=Wg(),Aq=Id(),Vg=Ed(),lq=Hn(),OIe=jv(),cq=SA(),Qd=Gg(),MIe=qv(),KIe=function(){function r(){}return r.prototype.initRecognizerEngine=function(e,t){if(this.className=(0,MIe.classNameFromInstance)(this),this.shortRuleNameToFull={},this.fullRuleNameToShort={},this.ruleShortNameIdx=256,this.tokenMatcher=Qd.tokenStructuredMatcherNoCategories,this.definedRulesNames=[],this.tokensMap={},this.isBackTrackingStack=[],this.RULE_STACK=[],this.RULE_OCCURRENCE_STACK=[],this.gastProductionsCache={},(0,Pr.has)(t,"serializedGrammar"))throw Error(`The Parser's configuration can no longer contain a property. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_6-0-0 + For Further details.`);if((0,Pr.isArray)(e)){if((0,Pr.isEmpty)(e))throw Error(`A Token Vocabulary cannot be empty. + Note that the first argument for the parser constructor + is no longer a Token vector (since v4.0).`);if(typeof e[0].startOffset=="number")throw Error(`The Parser constructor no longer accepts a token vector as the first argument. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_4-0-0 + For Further details.`)}if((0,Pr.isArray)(e))this.tokensMap=(0,Pr.reduce)(e,function(o,a){return o[a.name]=a,o},{});else if((0,Pr.has)(e,"modes")&&(0,Pr.every)((0,Pr.flatten)((0,Pr.values)(e.modes)),Qd.isTokenType)){var i=(0,Pr.flatten)((0,Pr.values)(e.modes)),n=(0,Pr.uniq)(i);this.tokensMap=(0,Pr.reduce)(n,function(o,a){return o[a.name]=a,o},{})}else if((0,Pr.isObject)(e))this.tokensMap=(0,Pr.cloneObj)(e);else throw new Error(" argument must be An Array of Token constructors, A dictionary of Token constructors or an IMultiModeLexerDefinition");this.tokensMap.EOF=cq.EOF;var s=(0,Pr.every)((0,Pr.values)(e),function(o){return(0,Pr.isEmpty)(o.categoryMatches)});this.tokenMatcher=s?Qd.tokenStructuredMatcherNoCategories:Qd.tokenStructuredMatcher,(0,Qd.augmentTokenTypes)((0,Pr.values)(this.tokensMap))},r.prototype.defineRule=function(e,t,i){if(this.selfAnalysisDone)throw Error("Grammar rule <"+e+`> may not be defined after the 'performSelfAnalysis' method has been called' +Make sure that all grammar rule definitions are done before 'performSelfAnalysis' is called.`);var n=(0,Pr.has)(i,"resyncEnabled")?i.resyncEnabled:lq.DEFAULT_RULE_CONFIG.resyncEnabled,s=(0,Pr.has)(i,"recoveryValueFunc")?i.recoveryValueFunc:lq.DEFAULT_RULE_CONFIG.recoveryValueFunc,o=this.ruleShortNameIdx<t},r.prototype.orInternal=function(e,t){var i=this.getKeyForAutomaticLookahead(Gn.OR_IDX,t),n=(0,Pr.isArray)(e)?e:e.DEF,s=this.getLaFuncFromCache(i),o=s.call(this,n);if(o!==void 0){var a=n[o];return a.ALT.call(this)}this.raiseNoAltException(t,e.ERR_MSG)},r.prototype.ruleFinallyStateUpdate=function(){if(this.RULE_STACK.pop(),this.RULE_OCCURRENCE_STACK.pop(),this.cstFinallyStateUpdate(),this.RULE_STACK.length===0&&this.isAtEndOfInput()===!1){var e=this.LA(1),t=this.errorMessageProvider.buildNotAllInputParsedMessage({firstRedundant:e,ruleName:this.getCurrRuleFullName()});this.SAVE_ERROR(new ly.NotAllInputParsedException(t,e))}},r.prototype.subruleInternal=function(e,t,i){var n;try{var s=i!==void 0?i.ARGS:void 0;return n=e.call(this,t,s),this.cstPostNonTerminal(n,i!==void 0&&i.LABEL!==void 0?i.LABEL:e.ruleName),n}catch(o){this.subruleInternalError(o,i,e.ruleName)}},r.prototype.subruleInternalError=function(e,t,i){throw(0,ly.isRecognitionException)(e)&&e.partialCstResult!==void 0&&(this.cstPostNonTerminal(e.partialCstResult,t!==void 0&&t.LABEL!==void 0?t.LABEL:i),delete e.partialCstResult),e},r.prototype.consumeInternal=function(e,t,i){var n;try{var s=this.LA(1);this.tokenMatcher(s,e)===!0?(this.consumeToken(),n=s):this.consumeInternalError(e,s,i)}catch(o){n=this.consumeInternalRecovery(e,t,o)}return this.cstPostTerminal(i!==void 0&&i.LABEL!==void 0?i.LABEL:e.name,n),n},r.prototype.consumeInternalError=function(e,t,i){var n,s=this.LA(0);throw i!==void 0&&i.ERR_MSG?n=i.ERR_MSG:n=this.errorMessageProvider.buildMismatchTokenMessage({expected:e,actual:t,previous:s,ruleName:this.getCurrRuleFullName()}),this.SAVE_ERROR(new ly.MismatchedTokenException(n,t,s))},r.prototype.consumeInternalRecovery=function(e,t,i){if(this.recoveryEnabled&&i.name==="MismatchedTokenException"&&!this.isBackTracking()){var n=this.getFollowsForInRuleRecovery(e,t);try{return this.tryInRuleRecovery(e,n)}catch(s){throw s.name===OIe.IN_RULE_RECOVERY_EXCEPTION?i:s}}else throw i},r.prototype.saveRecogState=function(){var e=this.errors,t=(0,Pr.cloneArr)(this.RULE_STACK);return{errors:e,lexerState:this.exportLexerState(),RULE_STACK:t,CST_STACK:this.CST_STACK}},r.prototype.reloadRecogState=function(e){this.errors=e.errors,this.importLexerState(e.lexerState),this.RULE_STACK=e.RULE_STACK},r.prototype.ruleInvocationStateUpdate=function(e,t,i){this.RULE_OCCURRENCE_STACK.push(i),this.RULE_STACK.push(e),this.cstInvocationStateUpdate(t,e)},r.prototype.isBackTracking=function(){return this.isBackTrackingStack.length!==0},r.prototype.getCurrRuleFullName=function(){var e=this.getLastExplicitRuleShortName();return this.shortRuleNameToFull[e]},r.prototype.shortRuleNameToFullName=function(e){return this.shortRuleNameToFull[e]},r.prototype.isAtEndOfInput=function(){return this.tokenMatcher(this.LA(1),cq.EOF)},r.prototype.reset=function(){this.resetLexerState(),this.isBackTrackingStack=[],this.errors=[],this.RULE_STACK=[],this.CST_STACK=[],this.RULE_OCCURRENCE_STACK=[]},r}();cy.RecognizerEngine=KIe});var fq=w(uy=>{"use strict";Object.defineProperty(uy,"__esModule",{value:!0});uy.ErrorHandler=void 0;var zv=Wg(),Vv=Gt(),gq=Id(),UIe=Hn(),HIe=function(){function r(){}return r.prototype.initErrorHandler=function(e){this._errors=[],this.errorMessageProvider=(0,Vv.has)(e,"errorMessageProvider")?e.errorMessageProvider:UIe.DEFAULT_PARSER_CONFIG.errorMessageProvider},r.prototype.SAVE_ERROR=function(e){if((0,zv.isRecognitionException)(e))return e.context={ruleStack:this.getHumanReadableRuleStack(),ruleOccurrenceStack:(0,Vv.cloneArr)(this.RULE_OCCURRENCE_STACK)},this._errors.push(e),e;throw Error("Trying to save an Error which is not a RecognitionException")},Object.defineProperty(r.prototype,"errors",{get:function(){return(0,Vv.cloneArr)(this._errors)},set:function(e){this._errors=e},enumerable:!1,configurable:!0}),r.prototype.raiseEarlyExitException=function(e,t,i){for(var n=this.getCurrRuleFullName(),s=this.getGAstProductions()[n],o=(0,gq.getLookaheadPathsForOptionalProd)(e,s,t,this.maxLookahead),a=o[0],l=[],c=1;c<=this.maxLookahead;c++)l.push(this.LA(c));var u=this.errorMessageProvider.buildEarlyExitMessage({expectedIterationPaths:a,actual:l,previous:this.LA(0),customUserDescription:i,ruleName:n});throw this.SAVE_ERROR(new zv.EarlyExitException(u,this.LA(1),this.LA(0)))},r.prototype.raiseNoAltException=function(e,t){for(var i=this.getCurrRuleFullName(),n=this.getGAstProductions()[i],s=(0,gq.getLookaheadPathsForOr)(e,n,this.maxLookahead),o=[],a=1;a<=this.maxLookahead;a++)o.push(this.LA(a));var l=this.LA(0),c=this.errorMessageProvider.buildNoViableAltMessage({expectedPathsPerAlt:s,actual:o,previous:l,customUserDescription:t,ruleName:this.getCurrRuleFullName()});throw this.SAVE_ERROR(new zv.NoViableAltException(c,this.LA(1),l))},r}();uy.ErrorHandler=HIe});var dq=w(gy=>{"use strict";Object.defineProperty(gy,"__esModule",{value:!0});gy.ContentAssist=void 0;var hq=Ed(),pq=Gt(),GIe=function(){function r(){}return r.prototype.initContentAssist=function(){},r.prototype.computeContentAssist=function(e,t){var i=this.gastProductionsCache[e];if((0,pq.isUndefined)(i))throw Error("Rule ->"+e+"<- does not exist in this grammar.");return(0,hq.nextPossibleTokensAfter)([i],t,this.tokenMatcher,this.maxLookahead)},r.prototype.getNextPossibleTokenTypes=function(e){var t=(0,pq.first)(e.ruleStack),i=this.getGAstProductions(),n=i[t],s=new hq.NextAfterTokenWalker(n,e).startWalking();return s},r}();gy.ContentAssist=GIe});var Qq=w(py=>{"use strict";Object.defineProperty(py,"__esModule",{value:!0});py.GastRecorder=void 0;var En=Gt(),Ro=dn(),YIe=gd(),Iq=Gg(),yq=SA(),jIe=Hn(),qIe=ny(),hy={description:"This Object indicates the Parser is during Recording Phase"};Object.freeze(hy);var Cq=!0,mq=Math.pow(2,qIe.BITS_FOR_OCCURRENCE_IDX)-1,wq=(0,yq.createToken)({name:"RECORDING_PHASE_TOKEN",pattern:YIe.Lexer.NA});(0,Iq.augmentTokenTypes)([wq]);var Bq=(0,yq.createTokenInstance)(wq,`This IToken indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,-1,-1,-1,-1,-1,-1);Object.freeze(Bq);var JIe={name:`This CSTNode indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,children:{}},WIe=function(){function r(){}return r.prototype.initGastRecorder=function(e){this.recordingProdStack=[],this.RECORDING_PHASE=!1},r.prototype.enableRecording=function(){var e=this;this.RECORDING_PHASE=!0,this.TRACE_INIT("Enable Recording",function(){for(var t=function(n){var s=n>0?n:"";e["CONSUME"+s]=function(o,a){return this.consumeInternalRecord(o,n,a)},e["SUBRULE"+s]=function(o,a){return this.subruleInternalRecord(o,n,a)},e["OPTION"+s]=function(o){return this.optionInternalRecord(o,n)},e["OR"+s]=function(o){return this.orInternalRecord(o,n)},e["MANY"+s]=function(o){this.manyInternalRecord(n,o)},e["MANY_SEP"+s]=function(o){this.manySepFirstInternalRecord(n,o)},e["AT_LEAST_ONE"+s]=function(o){this.atLeastOneInternalRecord(n,o)},e["AT_LEAST_ONE_SEP"+s]=function(o){this.atLeastOneSepFirstInternalRecord(n,o)}},i=0;i<10;i++)t(i);e.consume=function(n,s,o){return this.consumeInternalRecord(s,n,o)},e.subrule=function(n,s,o){return this.subruleInternalRecord(s,n,o)},e.option=function(n,s){return this.optionInternalRecord(s,n)},e.or=function(n,s){return this.orInternalRecord(s,n)},e.many=function(n,s){this.manyInternalRecord(n,s)},e.atLeastOne=function(n,s){this.atLeastOneInternalRecord(n,s)},e.ACTION=e.ACTION_RECORD,e.BACKTRACK=e.BACKTRACK_RECORD,e.LA=e.LA_RECORD})},r.prototype.disableRecording=function(){var e=this;this.RECORDING_PHASE=!1,this.TRACE_INIT("Deleting Recording methods",function(){for(var t=0;t<10;t++){var i=t>0?t:"";delete e["CONSUME"+i],delete e["SUBRULE"+i],delete e["OPTION"+i],delete e["OR"+i],delete e["MANY"+i],delete e["MANY_SEP"+i],delete e["AT_LEAST_ONE"+i],delete e["AT_LEAST_ONE_SEP"+i]}delete e.consume,delete e.subrule,delete e.option,delete e.or,delete e.many,delete e.atLeastOne,delete e.ACTION,delete e.BACKTRACK,delete e.LA})},r.prototype.ACTION_RECORD=function(e){},r.prototype.BACKTRACK_RECORD=function(e,t){return function(){return!0}},r.prototype.LA_RECORD=function(e){return jIe.END_OF_FILE},r.prototype.topLevelRuleRecord=function(e,t){try{var i=new Ro.Rule({definition:[],name:e});return i.name=e,this.recordingProdStack.push(i),t.call(this),this.recordingProdStack.pop(),i}catch(n){if(n.KNOWN_RECORDER_ERROR!==!0)try{n.message=n.message+` + This error was thrown during the "grammar recording phase" For more info see: + https://chevrotain.io/docs/guide/internals.html#grammar-recording`}catch{throw n}throw n}},r.prototype.optionInternalRecord=function(e,t){return bd.call(this,Ro.Option,e,t)},r.prototype.atLeastOneInternalRecord=function(e,t){bd.call(this,Ro.RepetitionMandatory,t,e)},r.prototype.atLeastOneSepFirstInternalRecord=function(e,t){bd.call(this,Ro.RepetitionMandatoryWithSeparator,t,e,Cq)},r.prototype.manyInternalRecord=function(e,t){bd.call(this,Ro.Repetition,t,e)},r.prototype.manySepFirstInternalRecord=function(e,t){bd.call(this,Ro.RepetitionWithSeparator,t,e,Cq)},r.prototype.orInternalRecord=function(e,t){return zIe.call(this,e,t)},r.prototype.subruleInternalRecord=function(e,t,i){if(fy(t),!e||(0,En.has)(e,"ruleName")===!1){var n=new Error(" argument is invalid"+(" expecting a Parser method reference but got: <"+JSON.stringify(e)+">")+(` + inside top level rule: <`+this.recordingProdStack[0].name+">"));throw n.KNOWN_RECORDER_ERROR=!0,n}var s=(0,En.peek)(this.recordingProdStack),o=e.ruleName,a=new Ro.NonTerminal({idx:t,nonTerminalName:o,label:i==null?void 0:i.LABEL,referencedRule:void 0});return s.definition.push(a),this.outputCst?JIe:hy},r.prototype.consumeInternalRecord=function(e,t,i){if(fy(t),!(0,Iq.hasShortKeyProperty)(e)){var n=new Error(" argument is invalid"+(" expecting a TokenType reference but got: <"+JSON.stringify(e)+">")+(` + inside top level rule: <`+this.recordingProdStack[0].name+">"));throw n.KNOWN_RECORDER_ERROR=!0,n}var s=(0,En.peek)(this.recordingProdStack),o=new Ro.Terminal({idx:t,terminalType:e,label:i==null?void 0:i.LABEL});return s.definition.push(o),Bq},r}();py.GastRecorder=WIe;function bd(r,e,t,i){i===void 0&&(i=!1),fy(t);var n=(0,En.peek)(this.recordingProdStack),s=(0,En.isFunction)(e)?e:e.DEF,o=new r({definition:[],idx:t});return i&&(o.separator=e.SEP),(0,En.has)(e,"MAX_LOOKAHEAD")&&(o.maxLookahead=e.MAX_LOOKAHEAD),this.recordingProdStack.push(o),s.call(this),n.definition.push(o),this.recordingProdStack.pop(),hy}function zIe(r,e){var t=this;fy(e);var i=(0,En.peek)(this.recordingProdStack),n=(0,En.isArray)(r)===!1,s=n===!1?r:r.DEF,o=new Ro.Alternation({definition:[],idx:e,ignoreAmbiguities:n&&r.IGNORE_AMBIGUITIES===!0});(0,En.has)(r,"MAX_LOOKAHEAD")&&(o.maxLookahead=r.MAX_LOOKAHEAD);var a=(0,En.some)(s,function(l){return(0,En.isFunction)(l.GATE)});return o.hasPredicates=a,i.definition.push(o),(0,En.forEach)(s,function(l){var c=new Ro.Alternative({definition:[]});o.definition.push(c),(0,En.has)(l,"IGNORE_AMBIGUITIES")?c.ignoreAmbiguities=l.IGNORE_AMBIGUITIES:(0,En.has)(l,"GATE")&&(c.ignoreAmbiguities=!0),t.recordingProdStack.push(c),l.ALT.call(t),t.recordingProdStack.pop()}),hy}function Eq(r){return r===0?"":""+r}function fy(r){if(r<0||r>mq){var e=new Error("Invalid DSL Method idx value: <"+r+`> + `+("Idx value must be a none negative value smaller than "+(mq+1)));throw e.KNOWN_RECORDER_ERROR=!0,e}}});var Sq=w(dy=>{"use strict";Object.defineProperty(dy,"__esModule",{value:!0});dy.PerformanceTracer=void 0;var bq=Gt(),VIe=Hn(),XIe=function(){function r(){}return r.prototype.initPerformanceTracer=function(e){if((0,bq.has)(e,"traceInitPerf")){var t=e.traceInitPerf,i=typeof t=="number";this.traceInitMaxIdent=i?t:1/0,this.traceInitPerf=i?t>0:t}else this.traceInitMaxIdent=0,this.traceInitPerf=VIe.DEFAULT_PARSER_CONFIG.traceInitPerf;this.traceInitIndent=-1},r.prototype.TRACE_INIT=function(e,t){if(this.traceInitPerf===!0){this.traceInitIndent++;var i=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <"+e+">");var n=(0,bq.timer)(t),s=n.time,o=n.value,a=s>10?console.warn:console.log;return this.traceInitIndent time: "+s+"ms"),this.traceInitIndent--,o}else return t()},r}();dy.PerformanceTracer=XIe});var vq=w(Cy=>{"use strict";Object.defineProperty(Cy,"__esModule",{value:!0});Cy.applyMixins=void 0;function _Ie(r,e){e.forEach(function(t){var i=t.prototype;Object.getOwnPropertyNames(i).forEach(function(n){if(n!=="constructor"){var s=Object.getOwnPropertyDescriptor(i,n);s&&(s.get||s.set)?Object.defineProperty(r.prototype,n,s):r.prototype[n]=t.prototype[n]}})})}Cy.applyMixins=_Ie});var Hn=w(dr=>{"use strict";var Dq=dr&&dr.__extends||function(){var r=function(e,t){return r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,n){i.__proto__=n}||function(i,n){for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(i[s]=n[s])},r(e,t)};return function(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");r(e,t);function i(){this.constructor=e}e.prototype=t===null?Object.create(t):(i.prototype=t.prototype,new i)}}();Object.defineProperty(dr,"__esModule",{value:!0});dr.EmbeddedActionsParser=dr.CstParser=dr.Parser=dr.EMPTY_ALT=dr.ParserDefinitionErrorType=dr.DEFAULT_RULE_CONFIG=dr.DEFAULT_PARSER_CONFIG=dr.END_OF_FILE=void 0;var Xi=Gt(),ZIe=fj(),xq=SA(),kq=Cd(),Pq=Kj(),$Ie=jv(),eye=Wj(),tye=iq(),rye=sq(),iye=aq(),nye=uq(),sye=fq(),oye=dq(),aye=Qq(),Aye=Sq(),lye=vq();dr.END_OF_FILE=(0,xq.createTokenInstance)(xq.EOF,"",NaN,NaN,NaN,NaN,NaN,NaN);Object.freeze(dr.END_OF_FILE);dr.DEFAULT_PARSER_CONFIG=Object.freeze({recoveryEnabled:!1,maxLookahead:3,dynamicTokensEnabled:!1,outputCst:!0,errorMessageProvider:kq.defaultParserErrorProvider,nodeLocationTracking:"none",traceInitPerf:!1,skipValidations:!1});dr.DEFAULT_RULE_CONFIG=Object.freeze({recoveryValueFunc:function(){},resyncEnabled:!0});var cye;(function(r){r[r.INVALID_RULE_NAME=0]="INVALID_RULE_NAME",r[r.DUPLICATE_RULE_NAME=1]="DUPLICATE_RULE_NAME",r[r.INVALID_RULE_OVERRIDE=2]="INVALID_RULE_OVERRIDE",r[r.DUPLICATE_PRODUCTIONS=3]="DUPLICATE_PRODUCTIONS",r[r.UNRESOLVED_SUBRULE_REF=4]="UNRESOLVED_SUBRULE_REF",r[r.LEFT_RECURSION=5]="LEFT_RECURSION",r[r.NONE_LAST_EMPTY_ALT=6]="NONE_LAST_EMPTY_ALT",r[r.AMBIGUOUS_ALTS=7]="AMBIGUOUS_ALTS",r[r.CONFLICT_TOKENS_RULES_NAMESPACE=8]="CONFLICT_TOKENS_RULES_NAMESPACE",r[r.INVALID_TOKEN_NAME=9]="INVALID_TOKEN_NAME",r[r.NO_NON_EMPTY_LOOKAHEAD=10]="NO_NON_EMPTY_LOOKAHEAD",r[r.AMBIGUOUS_PREFIX_ALTS=11]="AMBIGUOUS_PREFIX_ALTS",r[r.TOO_MANY_ALTS=12]="TOO_MANY_ALTS"})(cye=dr.ParserDefinitionErrorType||(dr.ParserDefinitionErrorType={}));function uye(r){return r===void 0&&(r=void 0),function(){return r}}dr.EMPTY_ALT=uye;var my=function(){function r(e,t){this.definitionErrors=[],this.selfAnalysisDone=!1;var i=this;if(i.initErrorHandler(t),i.initLexerAdapter(),i.initLooksAhead(t),i.initRecognizerEngine(e,t),i.initRecoverable(t),i.initTreeBuilder(t),i.initContentAssist(),i.initGastRecorder(t),i.initPerformanceTracer(t),(0,Xi.has)(t,"ignoredIssues"))throw new Error(`The IParserConfig property has been deprecated. + Please use the flag on the relevant DSL method instead. + See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES + For further details.`);this.skipValidations=(0,Xi.has)(t,"skipValidations")?t.skipValidations:dr.DEFAULT_PARSER_CONFIG.skipValidations}return r.performSelfAnalysis=function(e){throw Error("The **static** `performSelfAnalysis` method has been deprecated. \nUse the **instance** method with the same name instead.")},r.prototype.performSelfAnalysis=function(){var e=this;this.TRACE_INIT("performSelfAnalysis",function(){var t;e.selfAnalysisDone=!0;var i=e.className;e.TRACE_INIT("toFastProps",function(){(0,Xi.toFastProperties)(e)}),e.TRACE_INIT("Grammar Recording",function(){try{e.enableRecording(),(0,Xi.forEach)(e.definedRulesNames,function(s){var o=e[s],a=o.originalGrammarAction,l=void 0;e.TRACE_INIT(s+" Rule",function(){l=e.topLevelRuleRecord(s,a)}),e.gastProductionsCache[s]=l})}finally{e.disableRecording()}});var n=[];if(e.TRACE_INIT("Grammar Resolving",function(){n=(0,Pq.resolveGrammar)({rules:(0,Xi.values)(e.gastProductionsCache)}),e.definitionErrors=e.definitionErrors.concat(n)}),e.TRACE_INIT("Grammar Validations",function(){if((0,Xi.isEmpty)(n)&&e.skipValidations===!1){var s=(0,Pq.validateGrammar)({rules:(0,Xi.values)(e.gastProductionsCache),maxLookahead:e.maxLookahead,tokenTypes:(0,Xi.values)(e.tokensMap),errMsgProvider:kq.defaultGrammarValidatorErrorProvider,grammarName:i});e.definitionErrors=e.definitionErrors.concat(s)}}),(0,Xi.isEmpty)(e.definitionErrors)&&(e.recoveryEnabled&&e.TRACE_INIT("computeAllProdsFollows",function(){var s=(0,ZIe.computeAllProdsFollows)((0,Xi.values)(e.gastProductionsCache));e.resyncFollows=s}),e.TRACE_INIT("ComputeLookaheadFunctions",function(){e.preComputeLookaheadFunctions((0,Xi.values)(e.gastProductionsCache))})),!r.DEFER_DEFINITION_ERRORS_HANDLING&&!(0,Xi.isEmpty)(e.definitionErrors))throw t=(0,Xi.map)(e.definitionErrors,function(s){return s.message}),new Error(`Parser Definition Errors detected: + `+t.join(` +------------------------------- +`))})},r.DEFER_DEFINITION_ERRORS_HANDLING=!1,r}();dr.Parser=my;(0,lye.applyMixins)(my,[$Ie.Recoverable,eye.LooksAhead,tye.TreeBuilder,rye.LexerAdapter,nye.RecognizerEngine,iye.RecognizerApi,sye.ErrorHandler,oye.ContentAssist,aye.GastRecorder,Aye.PerformanceTracer]);var gye=function(r){Dq(e,r);function e(t,i){i===void 0&&(i=dr.DEFAULT_PARSER_CONFIG);var n=this,s=(0,Xi.cloneObj)(i);return s.outputCst=!0,n=r.call(this,t,s)||this,n}return e}(my);dr.CstParser=gye;var fye=function(r){Dq(e,r);function e(t,i){i===void 0&&(i=dr.DEFAULT_PARSER_CONFIG);var n=this,s=(0,Xi.cloneObj)(i);return s.outputCst=!1,n=r.call(this,t,s)||this,n}return e}(my);dr.EmbeddedActionsParser=fye});var Fq=w(Ey=>{"use strict";Object.defineProperty(Ey,"__esModule",{value:!0});Ey.createSyntaxDiagramsCode=void 0;var Rq=pv();function hye(r,e){var t=e===void 0?{}:e,i=t.resourceBase,n=i===void 0?"https://unpkg.com/chevrotain@"+Rq.VERSION+"/diagrams/":i,s=t.css,o=s===void 0?"https://unpkg.com/chevrotain@"+Rq.VERSION+"/diagrams/diagrams.css":s,a=` + + + + + +`,l=` + +`,c=` + + From 46d07bd445be02ccf41776108ccd8d9c6e654495 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 14:07:46 +0100 Subject: [PATCH 007/129] fix NavBar width --- src/Web/Pages/GamblingHelper.razor | 1 - src/Web/Pages/Stats.razor | 3 +-- src/Web/Pages/Stats.razor.cs | 19 +++++++++++-------- src/Web/Shared/Components/Navbar.razor | 4 ++-- src/Web/Shared/Components/Navbar.razor.css | 6 +++--- src/Web/Shared/MainLayout.razor | 1 + 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Web/Pages/GamblingHelper.razor b/src/Web/Pages/GamblingHelper.razor index df2a2584..f4d56619 100644 --- a/src/Web/Pages/GamblingHelper.razor +++ b/src/Web/Pages/GamblingHelper.razor @@ -2,7 +2,6 @@ PoE Gambling Helper -
icon
diff --git a/src/Web/Pages/Stats.razor b/src/Web/Pages/Stats.razor index c0cd6089..862ebdac 100644 --- a/src/Web/Pages/Stats.razor +++ b/src/Web/Pages/Stats.razor @@ -1,5 +1,4 @@ @page "/stats" -

Stats

@@ -12,7 +11,7 @@

- You have @LuckAdjective() @(LuckScore() > 0.9 ? "" : "luck"). + You have @LuckAdjective() @(LuckScore() > 0.5 ? "" : "luck").

Your Luck-Score is @LuckScore().Percent(). diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs index d4543a1f..81a02329 100644 --- a/src/Web/Pages/Stats.razor.cs +++ b/src/Web/Pages/Stats.razor.cs @@ -18,14 +18,17 @@ private string LuckAdjective() { return LuckScore() switch { - > 0.9 => "a streamer", - > 0.8 => "amazing", - > 0.7 => "good", - > 0.55 => "decent", - > 0.45 => "average", - > 0.3 => "bad", - > 0.2 => "terrible", - _ => "abominable" + > 0.9 => "contacts at GGG", + > 0.7 => "a hacked client", + > 0.5 => "a streamer client", + > 0.4 => "amazing", + > 0.3 => "good", + > 0.26 => "decent", + > 0.24 => "average", + > 0.2 => "bad", + > 0.1 => "terrible", + > 0 => "abominable", + _ => "negative" }; } } \ No newline at end of file diff --git a/src/Web/Shared/Components/Navbar.razor b/src/Web/Shared/Components/Navbar.razor index 40245837..33429d23 100644 --- a/src/Web/Shared/Components/Navbar.razor +++ b/src/Web/Shared/Components/Navbar.razor @@ -1,9 +1,9 @@ 

\ No newline at end of file diff --git a/src/Web/Shared/Components/Navbar.razor.css b/src/Web/Shared/Components/Navbar.razor.css index 3c7968e7..1c981cbb 100644 --- a/src/Web/Shared/Components/Navbar.razor.css +++ b/src/Web/Shared/Components/Navbar.razor.css @@ -2,13 +2,13 @@ display: flex; align-items: center; padding: 0 1rem; - margin: 0 -1.5rem; - width: 100vw; + margin: 0; + width: 100%; height: 5vh; background-color: var(--bs-body-bg-accent); } -nav ::deep a.test { +nav ::deep a { color: var(--bs-link-color); } diff --git a/src/Web/Shared/MainLayout.razor b/src/Web/Shared/MainLayout.razor index 712f49be..ee403a53 100644 --- a/src/Web/Shared/MainLayout.razor +++ b/src/Web/Shared/MainLayout.razor @@ -4,6 +4,7 @@
+
@Body
From 263bd0b0db349d976469137f422392048efef081 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 14:09:03 +0100 Subject: [PATCH 008/129] move ScrollToBottom to typescript --- PoEGamblingHelper.sln | 6 ----- src/Web/JavaScript/src/scroll-to-bottom.ts | 28 ++++++++++++++++++++++ src/Web/JavaScript/webpack.config.js | 3 ++- src/Web/wwwroot/index.html | 1 - src/Web/wwwroot/js/scroll-to-bottom.js | 7 ------ 5 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 src/Web/JavaScript/src/scroll-to-bottom.ts delete mode 100644 src/Web/wwwroot/js/scroll-to-bottom.js diff --git a/PoEGamblingHelper.sln b/PoEGamblingHelper.sln index 4197b025..7678fc08 100644 --- a/PoEGamblingHelper.sln +++ b/PoEGamblingHelper.sln @@ -18,8 +18,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Test", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Test", "test\Web.Test\Web.Test.csproj", "{72665095-42B7-4E4A-8019-1B6EF259C31B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp1", "BlazorApp1\BlazorApp1.csproj", "{1551E21D-51D1-43BB-91EF-6CAB9B797274}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -62,9 +60,5 @@ Global {72665095-42B7-4E4A-8019-1B6EF259C31B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.Build.0 = Release|Any CPU - {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1551E21D-51D1-43BB-91EF-6CAB9B797274}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Web/JavaScript/src/scroll-to-bottom.ts b/src/Web/JavaScript/src/scroll-to-bottom.ts new file mode 100644 index 00000000..5c89af19 --- /dev/null +++ b/src/Web/JavaScript/src/scroll-to-bottom.ts @@ -0,0 +1,28 @@ +declare global { + // noinspection JSUnusedGlobalSymbols + interface Window { + scrollInfoService: ScrollInfoService; + } +} + + +window.onscroll = () => { + console.log(window.scrollInfoService) + console.log(window.scrollY) + if (window.scrollInfoService !== undefined) + if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) + window.scrollInfoService.invokeMethodAsync('OnScrollToBottom', window.scrollY); +} + + +export function RegisterScrollInfoService(scrollInfoService: any) { + window.scrollInfoService = scrollInfoService; +} + +export function UnRegisterScrollInfoService() { + window.scrollInfoService = undefined; +} + +interface ScrollInfoService { + invokeMethodAsync: Function +} diff --git a/src/Web/JavaScript/webpack.config.js b/src/Web/JavaScript/webpack.config.js index 8c90a767..a956fdc4 100644 --- a/src/Web/JavaScript/webpack.config.js +++ b/src/Web/JavaScript/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { - entry: './src/index.ts', + // entry: './src/index.ts', + entry: ['./src/index.ts', './src/scroll-to-bottom.ts'], output: { path: path.resolve(__dirname, '../wwwroot/js/'), filename: 'index.bundle.js', diff --git a/src/Web/wwwroot/index.html b/src/Web/wwwroot/index.html index 97b2e47e..1c44cabc 100644 --- a/src/Web/wwwroot/index.html +++ b/src/Web/wwwroot/index.html @@ -32,7 +32,6 @@ - diff --git a/src/Web/wwwroot/js/scroll-to-bottom.js b/src/Web/wwwroot/js/scroll-to-bottom.js deleted file mode 100644 index 73f58c4e..00000000 --- a/src/Web/wwwroot/js/scroll-to-bottom.js +++ /dev/null @@ -1,7 +0,0 @@ -window.onscroll = () => { - if (window.scrollInfoService != null) - if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) - window.scrollInfoService.invokeMethodAsync('OnScrollToBottom', window.scrollY); -} -window.RegisterScrollInfoService = scrollInfoService => window.scrollInfoService = scrollInfoService; -window.UnRegisterScrollInfoService = () => window.scrollInfoService = undefined; \ No newline at end of file From 7277bc8cc31992affadc220045aee226014489af Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 14:56:58 +0100 Subject: [PATCH 009/129] move theme --- src/Web/JavaScript/package.json | 1 + .../JavaScript/src/{index.ts => pie-chart.ts} | 10 ++- src/Web/JavaScript/src/scroll-to-bottom.ts | 11 +-- src/Web/JavaScript/src/theme.ts | 50 ++++++++++++++ src/Web/JavaScript/webpack.config.js | 9 +-- src/Web/JavaScript/yarn.lock | 69 +++++++++++++++++++ src/Web/Web.csproj | 7 ++ src/Web/wwwroot/index.html | 1 - 8 files changed, 146 insertions(+), 12 deletions(-) rename src/Web/JavaScript/src/{index.ts => pie-chart.ts} (75%) create mode 100644 src/Web/JavaScript/src/theme.ts diff --git a/src/Web/JavaScript/package.json b/src/Web/JavaScript/package.json index 1614630b..4ec4e864 100644 --- a/src/Web/JavaScript/package.json +++ b/src/Web/JavaScript/package.json @@ -4,6 +4,7 @@ "main": "index.js", "license": "MIT", "devDependencies": { + "glob": "^9.1.2", "webpack": "^5.75.0", "webpack-cli": "^5.0.1" }, diff --git a/src/Web/JavaScript/src/index.ts b/src/Web/JavaScript/src/pie-chart.ts similarity index 75% rename from src/Web/JavaScript/src/index.ts rename to src/Web/JavaScript/src/pie-chart.ts index 16f67e64..09874a5a 100644 --- a/src/Web/JavaScript/src/index.ts +++ b/src/Web/JavaScript/src/pie-chart.ts @@ -1,7 +1,13 @@ import Chart from 'chart.js/auto'; -// noinspection JSUnusedGlobalSymbols -export function showChart(data: number[]) { +declare global { + // noinspection JSUnusedGlobalSymbols + interface Window { + showChart(data: number[]): void; + } +} + +window.showChart = (data: number[]) => { const ctx = document.getElementById('chart'); console.log(ctx) diff --git a/src/Web/JavaScript/src/scroll-to-bottom.ts b/src/Web/JavaScript/src/scroll-to-bottom.ts index 5c89af19..b82dc988 100644 --- a/src/Web/JavaScript/src/scroll-to-bottom.ts +++ b/src/Web/JavaScript/src/scroll-to-bottom.ts @@ -2,23 +2,24 @@ declare global { // noinspection JSUnusedGlobalSymbols interface Window { scrollInfoService: ScrollInfoService; + + RegisterScrollInfoService(scrollInfoService: ScrollInfoService): void; + + UnRegisterScrollInfoService(): void; } } window.onscroll = () => { - console.log(window.scrollInfoService) - console.log(window.scrollY) if (window.scrollInfoService !== undefined) if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) window.scrollInfoService.invokeMethodAsync('OnScrollToBottom', window.scrollY); } -export function RegisterScrollInfoService(scrollInfoService: any) { - window.scrollInfoService = scrollInfoService; -} +window.RegisterScrollInfoService = (scrollInfoService: any) => window.scrollInfoService = scrollInfoService; +window.UnRegisterScrollInfoService = UnRegisterScrollInfoService; export function UnRegisterScrollInfoService() { window.scrollInfoService = undefined; } diff --git a/src/Web/JavaScript/src/theme.ts b/src/Web/JavaScript/src/theme.ts new file mode 100644 index 00000000..4436e97f --- /dev/null +++ b/src/Web/JavaScript/src/theme.ts @@ -0,0 +1,50 @@ +declare global { + // noinspection JSUnusedGlobalSymbols + interface Window { + getTheme(): string; + + setTheme(theme: 'dark' | 'light'): void; + + useSavedTheme(): void; + } +} + +window.getTheme = () => localStorage.getItem('theme'); + +window.setTheme = (theme: 'dark' | 'light') => { + const themePath = `css/theme.${theme}.css`; + if (!CheckUrl(themePath)) return; + + const previousElements = document.head.querySelectorAll('[data-theme]'); + + const element = document.createElement('link'); + element.dataset.theme = ''; + element.rel = 'stylesheet'; + element.type = 'text/css'; + element.href = themePath; + element.onload = () => previousElements.forEach(e => e.parentNode?.removeChild(e)); + document.head.appendChild(element); + + localStorage.setItem('theme', theme); +} + +window.useSavedTheme = useSavedTheme; + +export function useSavedTheme() { + const theme = localStorage.getItem('theme'); + if (theme !== 'dark' && theme !== 'light') { + localStorage.removeItem('theme'); + return; + } + window.setTheme(theme !== null ? theme : 'dark'); +} + +function CheckUrl(url: string) { + let http; + if (window.XMLHttpRequest) http = new XMLHttpRequest(); // IE7+, Firefox, Chrome, Opera, Safari + else http = new ActiveXObject("Microsoft.XMLHTTP"); // IE6, IE5 + + http.open('HEAD', url, false); + http.send(); + return http.status !== 404; +} \ No newline at end of file diff --git a/src/Web/JavaScript/webpack.config.js b/src/Web/JavaScript/webpack.config.js index a956fdc4..25b71290 100644 --- a/src/Web/JavaScript/webpack.config.js +++ b/src/Web/JavaScript/webpack.config.js @@ -1,10 +1,11 @@ -const path = require('path'); +const glob = require('glob'); +const path = require('path'); +console.log(glob.globSync("./src/**/*.ts").map(e => `\./${e}`)) module.exports = { - // entry: './src/index.ts', - entry: ['./src/index.ts', './src/scroll-to-bottom.ts'], + entry: glob.globSync("./src/**/*.ts").map(e => `\./${e}`), output: { path: path.resolve(__dirname, '../wwwroot/js/'), - filename: 'index.bundle.js', + filename: 'bundle.js', libraryTarget: 'window', }, resolve: { diff --git a/src/Web/JavaScript/yarn.lock b/src/Web/JavaScript/yarn.lock index 504a65b8..4d8427e4 100644 --- a/src/Web/JavaScript/yarn.lock +++ b/src/Web/JavaScript/yarn.lock @@ -322,6 +322,7 @@ __metadata: resolution: "JavaScript@workspace:." dependencies: chart.js: ^4.2.1 + glob: ^9.1.2 source-map-loader: ^4.0.1 ts-loader: ^9.4.2 typescript: ^4.9.5 @@ -385,6 +386,22 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + "braces@npm:^3.0.2": version: 3.0.2 resolution: "braces@npm:3.0.2" @@ -627,6 +644,13 @@ __metadata: languageName: node linkType: hard +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -641,6 +665,18 @@ __metadata: languageName: node linkType: hard +"glob@npm:^9.1.2": + version: 9.1.2 + resolution: "glob@npm:9.1.2" + dependencies: + fs.realpath: ^1.0.0 + minimatch: ^7.4.1 + minipass: ^4.2.4 + path-scurry: ^1.5.0 + checksum: 8c0bcd729b5783c340553966a25728a6c6e53275b825b4bf3be52bc2d6d31ea39d55d3c7e989edfc541f5a0ba7421b8616788f55367dd8551aac6cebbf7491ab + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" @@ -788,6 +824,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^7.14.1": + version: 7.18.1 + resolution: "lru-cache@npm:7.18.1" + checksum: ab0ec1360c552f1ffa54b1eaf0026126c5116a07bee156b92d1e971f4c8c88e9160f0fad4ab6fed4e0fdea84f25a4590ece085bc57ed9ab1d90b17f0b138c556 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -821,6 +864,22 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^7.4.1": + version: 7.4.2 + resolution: "minimatch@npm:7.4.2" + dependencies: + brace-expansion: ^2.0.1 + checksum: 9e341b04e69d5ab03e4206dcb61c8a158e3b8709628bf5e1a4eaa9f3b72c0ba925e24ad959b1f6ce6835caa5a927131d5087fae6836b69e7d99d7d5e63ef0bd8 + languageName: node + linkType: hard + +"minipass@npm:^4.0.2, minipass@npm:^4.2.4": + version: 4.2.4 + resolution: "minipass@npm:4.2.4" + checksum: c664f2ae4401408d1e7a6e4f50aca45f87b1b0634bc9261136df5c378e313e77355765f73f59c4a5abcadcdf43d83fcd3eb14e4a7cdcce8e36508e2290345753 + languageName: node + linkType: hard + "neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" @@ -881,6 +940,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.5.0": + version: 1.6.1 + resolution: "path-scurry@npm:1.6.1" + dependencies: + lru-cache: ^7.14.1 + minipass: ^4.0.2 + checksum: 7ba57e823cb7bb879669a4e5e05a283cde1bb9e81b6d806b2609f8d8026d0aef08f4b655b17fc86b21c9c32807851bba95ca715db5ab0605fb13c7a3e9172e42 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 4b04101f..2e7472ba 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -17,6 +17,13 @@ + + + true + PreserveNewest + + + diff --git a/src/Web/wwwroot/index.html b/src/Web/wwwroot/index.html index 1c44cabc..44ec3db6 100644 --- a/src/Web/wwwroot/index.html +++ b/src/Web/wwwroot/index.html @@ -31,7 +31,6 @@ - From 895c5dc9f350072d19f98ddac1cb214bc7d18e0e Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 15:01:42 +0100 Subject: [PATCH 010/129] cleanup --- .gitignore | 2 +- src/Web/Web.csproj | 4 ++++ src/Web/wwwroot/index.html | 2 +- src/Web/wwwroot/js/theme.js | 37 ----------------------------------- src/Web/wwwroot/js/tooltip.js | 4 ++-- 5 files changed, 8 insertions(+), 41 deletions(-) delete mode 100644 src/Web/wwwroot/js/theme.js diff --git a/.gitignore b/.gitignore index ecbbb29e..87c8adb2 100644 --- a/.gitignore +++ b/.gitignore @@ -353,4 +353,4 @@ MigrationBackup/ # test commit -**/wwwroot/js/index.bundle.js* \ No newline at end of file +**/wwwroot/js/bundle.js* \ No newline at end of file diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 2e7472ba..95be30b3 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -22,6 +22,10 @@ true PreserveNewest + + true + PreserveNewest + diff --git a/src/Web/wwwroot/index.html b/src/Web/wwwroot/index.html index 44ec3db6..2e6a0cef 100644 --- a/src/Web/wwwroot/index.html +++ b/src/Web/wwwroot/index.html @@ -31,7 +31,7 @@ - + diff --git a/src/Web/wwwroot/js/theme.js b/src/Web/wwwroot/js/theme.js deleted file mode 100644 index ae4f4be2..00000000 --- a/src/Web/wwwroot/js/theme.js +++ /dev/null @@ -1,37 +0,0 @@ -// noinspection JSUnusedGlobalSymbols -function getTheme() { - return localStorage.getItem('theme'); -} - -function setTheme(theme) { - const themePath = `css/theme.${theme}.css`; - if (!CheckUrl(themePath)) return; - - const previousElements = document.head.querySelectorAll('[data-theme]'); - - const element = document.createElement('link'); - element.dataset.theme = ''; - element.rel = 'stylesheet'; - element.type = 'text/css'; - element.href = themePath; - element.onload = () => previousElements.forEach(e => e.parentNode?.removeChild(e)); - document.head.appendChild(element); - - localStorage.setItem('theme', theme); -} - -// noinspection JSUnusedGlobalSymbols -function useSavedTheme() { - const theme = localStorage.getItem('theme'); - setTheme(theme !== null ? theme : 'dark'); -} - -function CheckUrl(url) { - let http; - if (window.XMLHttpRequest) http = new XMLHttpRequest(); // IE7+, Firefox, Chrome, Opera, Safari - else http = new ActiveXObject("Microsoft.XMLHTTP"); // IE6, IE5 - - http.open('HEAD', url, false); - http.send(); - return http.status !== 404; -} \ No newline at end of file diff --git a/src/Web/wwwroot/js/tooltip.js b/src/Web/wwwroot/js/tooltip.js index f8263c92..ab5b2e55 100644 --- a/src/Web/wwwroot/js/tooltip.js +++ b/src/Web/wwwroot/js/tooltip.js @@ -1,6 +1,6 @@ -// noinspection JSUnusedGlobalSymbols,JSUnusedLocalSymbols - +// noinspection JSUnusedGlobalSymbols function addTooltips() { const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + // noinspection JSUnusedLocalSymbols const tooltipList = [...tooltipTriggerList].map(tooltipTriggerElement => new bootstrap.Tooltip(tooltipTriggerElement)); } \ No newline at end of file From 8edae9b3709432402313d4219257416c162a3138 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 2 Mar 2023 15:01:58 +0100 Subject: [PATCH 011/129] cleanup --- src/Web/Web.csproj | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 95be30b3..4b04101f 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -17,17 +17,6 @@ - - - true - PreserveNewest - - - true - PreserveNewest - - - From 54840fa56bc4cf491195aed89f8b248f7623a70b Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Wed, 8 Mar 2023 13:17:11 +0100 Subject: [PATCH 012/129] add UpdateService --- src/Web/Pages/GamblingHelper.razor | 10 +- src/Web/Pages/GamblingHelper.razor.cs | 75 ++---------- src/Web/Pages/GamblingHelper.razor.css | 17 --- src/Web/Pages/Stats.razor | 53 +++++---- src/Web/Program.cs | 1 + .../Services/Implementations/UpdateService.cs | 108 ++++++++++++++++++ src/Web/Services/Interfaces/IUpdateService.cs | 16 +++ src/Web/Shared/Components/Footer.razor | 5 + src/Web/Shared/Components/Footer.razor.cs | 37 ++++++ src/Web/Shared/Components/Footer.razor.css | 9 ++ src/Web/Shared/MainLayout.razor | 1 + 11 files changed, 221 insertions(+), 111 deletions(-) create mode 100644 src/Web/Services/Implementations/UpdateService.cs create mode 100644 src/Web/Services/Interfaces/IUpdateService.cs create mode 100644 src/Web/Shared/Components/Footer.razor create mode 100644 src/Web/Shared/Components/Footer.razor.cs create mode 100644 src/Web/Shared/Components/Footer.razor.css diff --git a/src/Web/Pages/GamblingHelper.razor b/src/Web/Pages/GamblingHelper.razor index f4d56619..e819aaa9 100644 --- a/src/Web/Pages/GamblingHelper.razor +++ b/src/Web/Pages/GamblingHelper.razor @@ -1,6 +1,6 @@ @page "/" -PoE Gambling Helper +Gambling Helper
icon @@ -13,7 +13,7 @@ - @if (_isUpdating || _firstLoad) + @if (UpdateService.IsUpdating() || _firstLoad) {
Loading... @@ -35,10 +35,4 @@
} } - -
\ No newline at end of file diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 39af9ad4..92ac5ba4 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -16,9 +16,6 @@ public partial class GamblingHelper : IAsyncDisposable private League _currentLeague = new(); private FilterValues _filterValues = new(); private bool _firstLoad = true; - private bool _isUpdating; - private DateTime _lastBackendUpdate = DateTime.MinValue; - private Task _loadGamblingDataTask = null!; private TempleCost _templeCost = new() { ChaosValue = new[] { 0m } }; [Inject] private IGemService GemService { get; set; } = default!; [Inject] private ITempleCostService TempleCostService { get; set; } = default!; @@ -26,8 +23,14 @@ public partial class GamblingHelper : IAsyncDisposable [Inject] private ILocalStorageService LocalStorage { get; set; } = default!; [Inject] private ILeagueService LeagueService { get; set; } = default!; [Inject] private IJSRuntime JsRuntime { get; set; } = null!; + [Inject] private IUpdateService UpdateService { get; set; } = null!; - private DateTime NextBackendUpdate() { return _lastBackendUpdate.AddMinutes(5); } + public async ValueTask DisposeAsync() + { + UpdateService.OnUiUpdate -= async _ => await InvokeAsync(StateHasChanged); + UpdateService.OnUpdate -= async _ => await LoadGamblingData(); + GC.SuppressFinalize(this); + } protected override async Task OnInitializedAsync() { @@ -35,29 +38,14 @@ protected override async Task OnInitializedAsync() var filterValues = await LocalStorage.GetItemAsync("GemDataQuery"); if (filterValues is not null) _filterValues = filterValues; - _loadGamblingDataTask = Task.Run(async () => - { - while (true) - { - while (_isUpdating || NextBackendUpdate() > DateTime.Now) - { - _tokenSource.Token.ThrowIfCancellationRequested(); - await InvokeAsync(StateHasChanged); - await Task.Delay(1000); - } - - _tokenSource.Token.ThrowIfCancellationRequested(); - await LoadGamblingData(); - await InvokeAsync(StateHasChanged); - } - }); + UpdateService.OnUpdate += async _ => await LoadGamblingData(); + UpdateService.OnUiUpdate += async _ => await InvokeAsync(StateHasChanged); } public async Task LoadGamblingData() { try { - _isUpdating = true; _firstLoad = false; await InvokeAsync(StateHasChanged); @@ -92,9 +80,6 @@ public async Task LoadGamblingData() } finally { - _lastBackendUpdate = DateTime.Now; - - _isUpdating = false; await InvokeAsync(StateHasChanged); await JsRuntime.InvokeVoidAsync("addTooltips"); } @@ -105,20 +90,9 @@ private async void OnFilterValuesChanged(FilterValues filterValues) _filterValues = filterValues; _currentGemPage = 0; _isOnLastPage = false; - await LoadGamblingData(); - } - - private string LastUpdateText() - { - return _lastBackendUpdate == DateTime.MinValue - ? "Never" - : _lastBackendUpdate < DateTime.Now.AddMinutes(-1) - ? $"{PassedMinutesSinceUpdate()} Minute{(PassedMinutesSinceUpdate() == 1 ? "" : "s")} ago" - : "Just now"; + await UpdateService.Update(); } - private int PassedMinutesSinceUpdate() { return (int)DateTime.Now.Subtract(_lastBackendUpdate).TotalMinutes; } - private async Task UpdateGems() { if (_isOnLastPage) return false; @@ -131,35 +105,6 @@ private async Task UpdateGems() return true; } - #region Dispose - - private readonly CancellationTokenSource _tokenSource = new(); - - public async ValueTask DisposeAsync() - { - await DisposeAsyncCore().ConfigureAwait(false); - GC.SuppressFinalize(this); - } - - protected virtual async ValueTask DisposeAsyncCore() - { - _tokenSource.Cancel(); - try - { - await _loadGamblingDataTask; - } - catch (OperationCanceledException) - { - } - finally - { - _loadGamblingDataTask.Dispose(); - _tokenSource.Dispose(); - } - } - - #endregion - #region OnScrollToBottom private readonly List _positionsY = new(); diff --git a/src/Web/Pages/GamblingHelper.razor.css b/src/Web/Pages/GamblingHelper.razor.css index 4cdfc635..45804ecd 100644 --- a/src/Web/Pages/GamblingHelper.razor.css +++ b/src/Web/Pages/GamblingHelper.razor.css @@ -1,21 +1,4 @@ -.vertical-content { - width: 100%; - height: 100%; - padding: 5rem; - display: flex; - flex-direction: column; - align-items: center; -} -.footer { - position: fixed; - bottom: 0; - left: 0; - background-color: var(--bs-body-bg); - display: flex; - width: 100%; - justify-content: space-between; -} h1 { font-weight: 500; diff --git a/src/Web/Pages/Stats.razor b/src/Web/Pages/Stats.razor index 862ebdac..b5fa0dea 100644 --- a/src/Web/Pages/Stats.razor +++ b/src/Web/Pages/Stats.razor @@ -1,27 +1,38 @@ @page "/stats" -

Stats

-
- - +Statistics Helper - - -
+
+ icon +
+

Path

+

of

+

Exile

+
+

STATISTICS HELPER

-
-

- You have @LuckAdjective() @(LuckScore() > 0.5 ? "" : "luck"). -

-

- Your Luck-Score is @LuckScore().Percent(). -

-

- Your Luck-Rank is 679120. -

-

- You made 420 Divines profit total. -

+
+ + - + + +
+ +
+

+ You have @LuckAdjective() @(LuckScore() > 0.5 ? "" : "luck"). +

+

+ Your Luck-Score is @LuckScore().Percent(). +

+

+ Your Luck-Rank is 679120. +

+

+ You made 420 Divines profit total. +

+ + +
\ No newline at end of file diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 1593c91a..67565736 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -27,5 +27,6 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddSingleton(); await builder.Build().RunAsync(); \ No newline at end of file diff --git a/src/Web/Services/Implementations/UpdateService.cs b/src/Web/Services/Implementations/UpdateService.cs new file mode 100644 index 00000000..bb6aaca6 --- /dev/null +++ b/src/Web/Services/Implementations/UpdateService.cs @@ -0,0 +1,108 @@ +using Web.Services.Interfaces; + +namespace Web.Services.Implementations; + +public class UpdateService : IUpdateService +{ + private readonly CancellationTokenSource _tokenSource = new(); + private bool _isUpdating; + private DateTime _lastUpdate = DateTime.MinValue; + private TimeSpan? _updateInterval = TimeSpan.FromSeconds(1); + private Task _updateTask = null!; + + #region public methods + + public void Init(Func updateAction, TimeSpan updateInterval, Func? uiUpdateAction) + { + _onUpdate += async _ => await updateAction.Invoke(); + UpdateInterval = updateInterval; + if (uiUpdateAction is not null) OnUiUpdate += async _ => await uiUpdateAction.Invoke(); + Init(); + } + + public void Init() + { + _updateTask = Task.Run(async () => + { + while (true) + { + if (UpdateInterval is not null) + while (_isUpdating || NextUpdate() > DateTime.Now) + { + _tokenSource.Token.ThrowIfCancellationRequested(); + if (OnUiUpdate is not null) await OnUiUpdate.Invoke(this); + await Task.Delay(1000); + } + + _tokenSource.Token.ThrowIfCancellationRequested(); + await Update(); + } + }); + } + + public async Task Update() + { + if (_onUpdate is null) return; + _isUpdating = true; + await _onUpdate.Invoke(this); + _lastUpdate = DateTime.Now; + _isUpdating = false; + if (OnUiUpdate is not null) await OnUiUpdate.Invoke(this); + } + + private event AsyncEventHandler? _onUpdate; + private event AsyncEventHandler? OnUiUpdate; + + AsyncEventHandler? IUpdateService.OnUpdate + { + get => _onUpdate; + set => _onUpdate = value; + } + + AsyncEventHandler? IUpdateService.OnUiUpdate + { + get => OnUiUpdate; + set => OnUiUpdate = value; + } + + public TimeSpan? UpdateInterval + { + get => _updateInterval; + set => _updateInterval = value; + } + + public DateTime? NextUpdate() { return _updateInterval is null ? null : _lastUpdate.Add(_updateInterval.Value); } + public DateTime LastUpdate() { return _lastUpdate; } + public bool IsUpdating() { return _isUpdating; } + + public delegate Task AsyncEventHandler(object sender); + + #endregion + + #region Dispose + + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore().ConfigureAwait(false); + GC.SuppressFinalize(this); + } + + protected virtual async ValueTask DisposeAsyncCore() + { + _tokenSource.Cancel(); + try + { + await _updateTask; + } + catch (OperationCanceledException) + { + } + finally + { + _updateTask.Dispose(); + _tokenSource.Dispose(); + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Web/Services/Interfaces/IUpdateService.cs b/src/Web/Services/Interfaces/IUpdateService.cs new file mode 100644 index 00000000..c3fc1128 --- /dev/null +++ b/src/Web/Services/Interfaces/IUpdateService.cs @@ -0,0 +1,16 @@ +using Web.Services.Implementations; + +namespace Web.Services.Interfaces; + +public interface IUpdateService : IAsyncDisposable +{ + TimeSpan? UpdateInterval { get; set; } + UpdateService.AsyncEventHandler? OnUpdate { get; set; } + UpdateService.AsyncEventHandler? OnUiUpdate { get; set; } + void Init(Func updateAction, TimeSpan updateInterval, Func? uiUpdateAction); + void Init(); + Task Update(); + DateTime? NextUpdate(); + DateTime LastUpdate(); + bool IsUpdating(); +} \ No newline at end of file diff --git a/src/Web/Shared/Components/Footer.razor b/src/Web/Shared/Components/Footer.razor new file mode 100644 index 00000000..dae2af7c --- /dev/null +++ b/src/Web/Shared/Components/Footer.razor @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/src/Web/Shared/Components/Footer.razor.cs b/src/Web/Shared/Components/Footer.razor.cs new file mode 100644 index 00000000..db4b56ac --- /dev/null +++ b/src/Web/Shared/Components/Footer.razor.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Components; +using Web.Services.Interfaces; + +namespace Web.Shared.Components; + +public partial class Footer : IAsyncDisposable +{ + [Inject] private IUpdateService UpdateService { get; set; } = null!; + + public async ValueTask DisposeAsync() + { + await UpdateService.DisposeAsync().ConfigureAwait(false); + GC.SuppressFinalize(this); + } + + private string LastUpdateText() + { + Console.WriteLine("LAST UPDATE TEXT " + DateTime.Now); + return UpdateService.LastUpdate() == DateTime.MinValue + ? "Never" + : UpdateService.LastUpdate() < DateTime.Now.AddMinutes(-1) + ? $"{PassedMinutesSinceUpdate()} Minute{(PassedMinutesSinceUpdate() == 1 ? "" : "s")} ago" + : "Just now"; + } + + private int PassedMinutesSinceUpdate() + { + return (int)DateTime.Now.Subtract(UpdateService.LastUpdate()).TotalMinutes; + } + + protected override void OnInitialized() + { + UpdateService.UpdateInterval = TimeSpan.FromMinutes(5); + UpdateService.OnUiUpdate += async _ => await InvokeAsync(StateHasChanged); + UpdateService.Init(); + } +} \ No newline at end of file diff --git a/src/Web/Shared/Components/Footer.razor.css b/src/Web/Shared/Components/Footer.razor.css new file mode 100644 index 00000000..50a6e58e --- /dev/null +++ b/src/Web/Shared/Components/Footer.razor.css @@ -0,0 +1,9 @@ +.footer { + position: fixed; + bottom: 0; + left: 0; + background-color: var(--bs-body-bg); + display: flex; + width: 100%; + justify-content: space-between; +} \ No newline at end of file diff --git a/src/Web/Shared/MainLayout.razor b/src/Web/Shared/MainLayout.razor index ee403a53..392c318f 100644 --- a/src/Web/Shared/MainLayout.razor +++ b/src/Web/Shared/MainLayout.razor @@ -8,6 +8,7 @@
@Body
+
From cf30d9f5ab224710ab7b902745bc23d18bbd5229 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Wed, 8 Mar 2023 13:26:44 +0100 Subject: [PATCH 013/129] cleanup --- src/Web/Pages/GamblingHelper.razor.cs | 6 +++--- src/Web/Pages/Stats.razor.cs | 21 +++++++++++++++++++-- src/Web/Shared/Components/Footer.razor.cs | 1 - 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 92ac5ba4..a86017af 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -9,7 +9,7 @@ namespace Web.Pages; -public partial class GamblingHelper : IAsyncDisposable +public partial class GamblingHelper : IDisposable { private readonly List _gems = new(); private List _currency = new(); @@ -25,10 +25,10 @@ public partial class GamblingHelper : IAsyncDisposable [Inject] private IJSRuntime JsRuntime { get; set; } = null!; [Inject] private IUpdateService UpdateService { get; set; } = null!; - public async ValueTask DisposeAsync() + public void Dispose() { - UpdateService.OnUiUpdate -= async _ => await InvokeAsync(StateHasChanged); UpdateService.OnUpdate -= async _ => await LoadGamblingData(); + UpdateService.OnUiUpdate -= async _ => await InvokeAsync(StateHasChanged); GC.SuppressFinalize(this); } diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs index 81a02329..30e8558f 100644 --- a/src/Web/Pages/Stats.razor.cs +++ b/src/Web/Pages/Stats.razor.cs @@ -1,9 +1,20 @@ -namespace Web.Pages; +using Microsoft.AspNetCore.Components; +using Web.Services.Interfaces; -public partial class Stats +namespace Web.Pages; + +public partial class Stats : IDisposable { private readonly int[] _data = { Random.Shared.Next(10000), Random.Shared.Next(10000), Random.Shared.Next(10000) }; private bool _isMyAccountSelected = true; + [Inject] private IUpdateService UpdateService { get; set; } = null!; + + public void Dispose() + { + //UpdateService.OnUpdate -= async _ => await LoadGamblingData(); + UpdateService.OnUiUpdate -= async _ => await InvokeAsync(StateHasChanged); + GC.SuppressFinalize(this); + } private double LuckScore() { @@ -14,6 +25,12 @@ private double LuckScore() : 0; } + protected override void OnInitialized() + { + //UpdateService.OnUpdate += async _ => await LoadGamblingData(); + UpdateService.OnUiUpdate += async _ => await InvokeAsync(StateHasChanged); + } + private string LuckAdjective() { return LuckScore() switch diff --git a/src/Web/Shared/Components/Footer.razor.cs b/src/Web/Shared/Components/Footer.razor.cs index db4b56ac..310832db 100644 --- a/src/Web/Shared/Components/Footer.razor.cs +++ b/src/Web/Shared/Components/Footer.razor.cs @@ -15,7 +15,6 @@ public async ValueTask DisposeAsync() private string LastUpdateText() { - Console.WriteLine("LAST UPDATE TEXT " + DateTime.Now); return UpdateService.LastUpdate() == DateTime.MinValue ? "Never" : UpdateService.LastUpdate() < DateTime.Now.AddMinutes(-1) From 415c868fa19f64017acd07cfad0134b2af794774 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Wed, 8 Mar 2023 13:57:15 +0100 Subject: [PATCH 014/129] fix update --- src/Web/Pages/GamblingHelper.razor.cs | 1 + src/Web/Pages/Stats.razor.cs | 1 - src/Web/Services/Implementations/UpdateService.cs | 8 +++----- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index a86017af..faba43c5 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -40,6 +40,7 @@ protected override async Task OnInitializedAsync() UpdateService.OnUpdate += async _ => await LoadGamblingData(); UpdateService.OnUiUpdate += async _ => await InvokeAsync(StateHasChanged); + await UpdateService.Update(); } public async Task LoadGamblingData() diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs index 30e8558f..dffecacd 100644 --- a/src/Web/Pages/Stats.razor.cs +++ b/src/Web/Pages/Stats.razor.cs @@ -18,7 +18,6 @@ public void Dispose() private double LuckScore() { - Console.WriteLine(Random.Shared.Next(10000)); Console.WriteLine(_data[2]); return _data.Length > 0 ? _data[2] / (double)_data.Sum() diff --git a/src/Web/Services/Implementations/UpdateService.cs b/src/Web/Services/Implementations/UpdateService.cs index bb6aaca6..b9401d31 100644 --- a/src/Web/Services/Implementations/UpdateService.cs +++ b/src/Web/Services/Implementations/UpdateService.cs @@ -9,6 +9,8 @@ public class UpdateService : IUpdateService private DateTime _lastUpdate = DateTime.MinValue; private TimeSpan? _updateInterval = TimeSpan.FromSeconds(1); private Task _updateTask = null!; + private event AsyncEventHandler? _onUpdate; + private event AsyncEventHandler? OnUiUpdate; #region public methods @@ -42,17 +44,13 @@ public void Init() public async Task Update() { - if (_onUpdate is null) return; _isUpdating = true; - await _onUpdate.Invoke(this); + if (_onUpdate is not null) await _onUpdate.Invoke(this); _lastUpdate = DateTime.Now; _isUpdating = false; if (OnUiUpdate is not null) await OnUiUpdate.Invoke(this); } - private event AsyncEventHandler? _onUpdate; - private event AsyncEventHandler? OnUiUpdate; - AsyncEventHandler? IUpdateService.OnUpdate { get => _onUpdate; From 90feb832b05870bb9831abee95c74dac03260f0c Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Wed, 8 Mar 2023 13:58:39 +0100 Subject: [PATCH 015/129] remove unused method --- src/Web/Services/Implementations/UpdateService.cs | 8 -------- src/Web/Services/Interfaces/IUpdateService.cs | 1 - 2 files changed, 9 deletions(-) diff --git a/src/Web/Services/Implementations/UpdateService.cs b/src/Web/Services/Implementations/UpdateService.cs index b9401d31..fad3661f 100644 --- a/src/Web/Services/Implementations/UpdateService.cs +++ b/src/Web/Services/Implementations/UpdateService.cs @@ -14,14 +14,6 @@ public class UpdateService : IUpdateService #region public methods - public void Init(Func updateAction, TimeSpan updateInterval, Func? uiUpdateAction) - { - _onUpdate += async _ => await updateAction.Invoke(); - UpdateInterval = updateInterval; - if (uiUpdateAction is not null) OnUiUpdate += async _ => await uiUpdateAction.Invoke(); - Init(); - } - public void Init() { _updateTask = Task.Run(async () => diff --git a/src/Web/Services/Interfaces/IUpdateService.cs b/src/Web/Services/Interfaces/IUpdateService.cs index c3fc1128..edfe6a56 100644 --- a/src/Web/Services/Interfaces/IUpdateService.cs +++ b/src/Web/Services/Interfaces/IUpdateService.cs @@ -7,7 +7,6 @@ public interface IUpdateService : IAsyncDisposable TimeSpan? UpdateInterval { get; set; } UpdateService.AsyncEventHandler? OnUpdate { get; set; } UpdateService.AsyncEventHandler? OnUiUpdate { get; set; } - void Init(Func updateAction, TimeSpan updateInterval, Func? uiUpdateAction); void Init(); Task Update(); DateTime? NextUpdate(); From ce1c21a40cc41e7af6003619ebaa46d2bea31568 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Wed, 8 Mar 2023 14:03:58 +0100 Subject: [PATCH 016/129] cleanup --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 87c8adb2..15d2d1e4 100644 --- a/.gitignore +++ b/.gitignore @@ -350,7 +350,4 @@ MigrationBackup/ **/.idea/ -# test commit - - **/wwwroot/js/bundle.js* \ No newline at end of file From 3af86415680ba276f347b6f0dcb80c7a0af1ff58 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 9 Mar 2023 15:50:21 +0100 Subject: [PATCH 017/129] fix imprint centering --- src/Web/Shared/Components/Footer.razor | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Web/Shared/Components/Footer.razor b/src/Web/Shared/Components/Footer.razor index dae2af7c..e32f8a9d 100644 --- a/src/Web/Shared/Components/Footer.razor +++ b/src/Web/Shared/Components/Footer.razor @@ -1,5 +1,11 @@  \ No newline at end of file From bac756737a1b140664fe7cda77e43fea169f2ec0 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 10 Mar 2023 13:27:07 +0100 Subject: [PATCH 018/129] idk what I did --- src/Api/Api.csproj | 8 +- src/Api/Controllers/StatsController.cs | 39 +++ .../Services/IApplicationDbContext.cs | 2 + src/Domain/Entity/Stats/CurrencyResult.cs | 7 + src/Domain/Entity/Stats/Result.cs | 11 + .../Migrations/20230309081719_2.0.Designer.cs | 228 ++++++++++++++++++ .../Migrations/20230309081719_2.0.cs | 73 ++++++ .../ApplicationDbContextModelSnapshot.cs | 59 +++++ .../Services/ApplicationDbContext.cs | 6 +- src/Web/Pages/Stats.razor | 2 +- 10 files changed, 427 insertions(+), 8 deletions(-) create mode 100644 src/Api/Controllers/StatsController.cs create mode 100644 src/Domain/Entity/Stats/CurrencyResult.cs create mode 100644 src/Domain/Entity/Stats/Result.cs create mode 100644 src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs create mode 100644 src/Infrastructure/Migrations/20230309081719_2.0.cs diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index bfc01259..5c4b6fa2 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -8,8 +8,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -17,8 +17,8 @@ - - + + diff --git a/src/Api/Controllers/StatsController.cs b/src/Api/Controllers/StatsController.cs new file mode 100644 index 00000000..0579783d --- /dev/null +++ b/src/Api/Controllers/StatsController.cs @@ -0,0 +1,39 @@ +using Application.Services; +using Domain.Entity; +using Domain.Entity.Stats; +using Domain.Exception; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OutputCaching; +using Microsoft.EntityFrameworkCore; + +namespace Api.Controllers; + +public class StatsController : ApiControllerBase +{ + private readonly IApplicationDbContextFactory _applicationDbContextFactory; + + public StatsController(IApplicationDbContextFactory applicationDbContextFactory) + { + _applicationDbContextFactory = applicationDbContextFactory; + } + + [HttpGet] + public async IAsyncEnumerable GetAll() + { + using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await foreach (var item in applicationDbContext.Result + .Include(r => r.CurrencyResult) + .Include(r => r.GemTradeData) + .AsAsyncEnumerable() + .ConfigureAwait(false)) + yield return item; + } + + [HttpPost] + public async Task Post(Result result) + { + using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await applicationDbContext.Result.AddAsync(result); + await applicationDbContext.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/src/Application/Services/IApplicationDbContext.cs b/src/Application/Services/IApplicationDbContext.cs index 0c0b0c2b..7d13590c 100644 --- a/src/Application/Services/IApplicationDbContext.cs +++ b/src/Application/Services/IApplicationDbContext.cs @@ -1,5 +1,6 @@ using Domain.Entity; using Domain.Entity.Gem; +using Domain.Entity.Stats; using Microsoft.EntityFrameworkCore; namespace Application.Services; @@ -11,6 +12,7 @@ public interface IApplicationDbContext : IDisposable DbSet TempleCost { get; } DbSet GemData { get; } DbSet GemTradeData { get; } + DbSet Result { get; } Task SaveChangesAsync(); void ClearTrackedEntities(); } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/CurrencyResult.cs b/src/Domain/Entity/Stats/CurrencyResult.cs new file mode 100644 index 00000000..9f325256 --- /dev/null +++ b/src/Domain/Entity/Stats/CurrencyResult.cs @@ -0,0 +1,7 @@ +namespace Domain.Entity.Stats; + +public class CurrencyResult: Abstract.Entity +{ + public string Name { get; set; } = string.Empty; + public decimal ChaosEquivalent { get; set; } +} \ No newline at end of file diff --git a/src/Domain/Entity/Stats/Result.cs b/src/Domain/Entity/Stats/Result.cs new file mode 100644 index 00000000..d0ea84b6 --- /dev/null +++ b/src/Domain/Entity/Stats/Result.cs @@ -0,0 +1,11 @@ +using Domain.Entity.Gem; + +namespace Domain.Entity.Stats; + +public class Result: Abstract.Entity +{ + + public GemTradeData GemTradeData { get; set; } + public decimal CurrencyValue { get; set; } + public CurrencyResult CurrencyResult { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs new file mode 100644 index 00000000..536bfe07 --- /dev/null +++ b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs @@ -0,0 +1,228 @@ +// +using System; +using Infrastructure.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230309081719_2.0")] + partial class _20 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entity.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ChaosEquivalent") + .HasColumnType("numeric"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("Domain.Entity.Gem.GemData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Icon") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("GemData"); + }); + + modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChaosValue") + .HasColumnType("numeric"); + + b.Property("Corrupted") + .HasColumnType("boolean"); + + b.Property("DetailsId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DivineValue") + .HasColumnType("numeric"); + + b.Property("ExaltedValue") + .HasColumnType("numeric"); + + b.Property("GemDataId") + .HasColumnType("uuid"); + + b.Property("GemLevel") + .HasColumnType("integer"); + + b.Property("GemQuality") + .HasColumnType("integer"); + + b.Property("ListingCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GemDataId"); + + b.ToTable("GemTradeData"); + }); + + modelBuilder.Entity("Domain.Entity.League", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("League"); + }); + + modelBuilder.Entity("Domain.Entity.Stats.CurrencyResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ChaosEquivalent") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("CurrencyResult"); + }); + + modelBuilder.Entity("Domain.Entity.Stats.Result", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrencyResultId") + .HasColumnType("text"); + + b.Property("CurrencyValue") + .HasColumnType("numeric"); + + b.Property("GemTradeDataId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CurrencyResultId"); + + b.HasIndex("GemTradeDataId"); + + b.ToTable("Result"); + }); + + modelBuilder.Entity("Domain.Entity.TempleCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChaosValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("TimeStamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("TempleCost"); + }); + + modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => + { + b.HasOne("Domain.Entity.Gem.GemData", null) + .WithMany("Gems") + .HasForeignKey("GemDataId"); + }); + + modelBuilder.Entity("Domain.Entity.Stats.Result", b => + { + b.HasOne("Domain.Entity.Stats.CurrencyResult", "CurrencyResult") + .WithMany() + .HasForeignKey("CurrencyResultId"); + + b.HasOne("Domain.Entity.Gem.GemTradeData", "GemTradeData") + .WithMany() + .HasForeignKey("GemTradeDataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrencyResult"); + + b.Navigation("GemTradeData"); + }); + + modelBuilder.Entity("Domain.Entity.Gem.GemData", b => + { + b.Navigation("Gems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.cs b/src/Infrastructure/Migrations/20230309081719_2.0.cs new file mode 100644 index 00000000..8da177c4 --- /dev/null +++ b/src/Infrastructure/Migrations/20230309081719_2.0.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class _20 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CurrencyResult", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + ChaosEquivalent = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CurrencyResult", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Result", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + GemTradeDataId = table.Column(type: "bigint", nullable: false), + CurrencyValue = table.Column(type: "numeric", nullable: false), + CurrencyResultId = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Result", x => x.Id); + table.ForeignKey( + name: "FK_Result_CurrencyResult_CurrencyResultId", + column: x => x.CurrencyResultId, + principalTable: "CurrencyResult", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Result_GemTradeData_GemTradeDataId", + column: x => x.GemTradeDataId, + principalTable: "GemTradeData", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Result_CurrencyResultId", + table: "Result", + column: "CurrencyResultId"); + + migrationBuilder.CreateIndex( + name: "IX_Result_GemTradeDataId", + table: "Result", + column: "GemTradeDataId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Result"); + + migrationBuilder.DropTable( + name: "CurrencyResult"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 01a3393d..16872562 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -131,6 +131,48 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("League"); }); + modelBuilder.Entity("Domain.Entity.Stats.CurrencyResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ChaosEquivalent") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("CurrencyResult"); + }); + + modelBuilder.Entity("Domain.Entity.Stats.Result", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrencyResultId") + .HasColumnType("text"); + + b.Property("CurrencyValue") + .HasColumnType("numeric"); + + b.Property("GemTradeDataId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CurrencyResultId"); + + b.HasIndex("GemTradeDataId"); + + b.ToTable("Result"); + }); + modelBuilder.Entity("Domain.Entity.TempleCost", b => { b.Property("Id") @@ -156,6 +198,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("GemDataId"); }); + modelBuilder.Entity("Domain.Entity.Stats.Result", b => + { + b.HasOne("Domain.Entity.Stats.CurrencyResult", "CurrencyResult") + .WithMany() + .HasForeignKey("CurrencyResultId"); + + b.HasOne("Domain.Entity.Gem.GemTradeData", "GemTradeData") + .WithMany() + .HasForeignKey("GemTradeDataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrencyResult"); + + b.Navigation("GemTradeData"); + }); + modelBuilder.Entity("Domain.Entity.Gem.GemData", b => { b.Navigation("Gems"); diff --git a/src/Infrastructure/Services/ApplicationDbContext.cs b/src/Infrastructure/Services/ApplicationDbContext.cs index 2a90c4cd..c7540268 100644 --- a/src/Infrastructure/Services/ApplicationDbContext.cs +++ b/src/Infrastructure/Services/ApplicationDbContext.cs @@ -1,13 +1,12 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using System.Text.Json; using Application.Services; using Domain.Entity; using Domain.Entity.Gem; +using Domain.Entity.Stats; using Microsoft.EntityFrameworkCore; namespace Infrastructure.Services; -[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")] public class ApplicationDbContext : DbContext, IApplicationDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } @@ -16,6 +15,7 @@ public ApplicationDbContext(DbContextOptions options) : ba public virtual DbSet TempleCost => Set(); public virtual DbSet GemData => Set(); public virtual DbSet GemTradeData => Set(); + public virtual DbSet Result => Set(); public Task SaveChangesAsync() { return base.SaveChangesAsync(); } public void ClearTrackedEntities() { ChangeTracker.Clear(); } diff --git a/src/Web/Pages/Stats.razor b/src/Web/Pages/Stats.razor index b5fa0dea..94d94ec6 100644 --- a/src/Web/Pages/Stats.razor +++ b/src/Web/Pages/Stats.razor @@ -21,7 +21,7 @@

- You have @LuckAdjective() @(LuckScore() > 0.5 ? "" : "luck"). + You have @LuckAdjective()@(LuckScore() > 0.5 ? "" : " luck").

Your Luck-Score is @LuckScore().Percent(). From cf2a14a2cadf278d1b4485f2a5b79071f6462eb1 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 10 Mar 2023 13:32:35 +0100 Subject: [PATCH 019/129] ew --- src/Web/Shared/Components/Footer.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Shared/Components/Footer.razor b/src/Web/Shared/Components/Footer.razor index e32f8a9d..d683495c 100644 --- a/src/Web/Shared/Components/Footer.razor +++ b/src/Web/Shared/Components/Footer.razor @@ -6,6 +6,6 @@ Imprint

-

Powered by poe.ninja

+

Powered by poe.ninja

\ No newline at end of file From f833b7badd759d1fe6a823f811397606a3ee3abd Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 28 Aug 2023 12:09:15 +0200 Subject: [PATCH 020/129] add gzip --- src/Web/nginx.conf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Web/nginx.conf b/src/Web/nginx.conf index 85c9b339..03ef7d07 100644 --- a/src/Web/nginx.conf +++ b/src/Web/nginx.conf @@ -2,9 +2,10 @@ events { } http { server { include mime.types; # necessary for CSS - - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + gzip on; + gzip_types text/plain application/xml image/png application/javascript text/css image/svg+xml; + gzip_proxied no-cache no-store private expired auth; listen 80 default_server; @@ -12,6 +13,7 @@ http { proxy_pass http://api/; } location / { + gzip_static on; root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } From 30b2105f1eee353daa0f3e68e84bbe983baa5870 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Tue, 29 Aug 2023 16:37:52 +0200 Subject: [PATCH 021/129] refactor to proper Clean Architecture --- Directory.Build.props | 7 + PoEGamblingHelper.sln | 6 + src/Api/AnalyticsMiddleware.cs | 26 ++++ src/Api/Controllers/ApiControllerBase.cs | 2 +- src/Api/Controllers/CurrencyController.cs | 19 ++- src/Api/Controllers/GemController.cs | 6 +- src/Api/Controllers/HttpUtilFunctions.cs | 2 +- src/Api/Controllers/LeagueController.cs | 8 +- src/Api/Controllers/StatsController.cs | 8 +- src/Api/Controllers/TempleController.cs | 7 +- .../Filters/HttpResponseExceptionFilter.cs | 2 +- src/Api/Program.cs | 8 +- src/Application/Application.csproj | 9 +- .../Exception/Abstract/HttpException.cs | 4 +- .../Abstract/InternalServerErrorException.cs | 4 +- .../Exception/Abstract/NotFoundException.cs | 4 +- .../Abstract/PoeGamblingHelperException.cs | 2 +- .../Exception/ApiDownException.cs | 6 +- .../Exception/Body/ExceptionId.cs | 2 +- .../Exception/Body/ExceptionType.cs | 2 +- .../Body/PoeGamblingHelperExceptionBody.cs | 2 +- .../Exception/NoLeagueDataException.cs | 6 +- .../Exception/NoTempleDataException.cs | 6 +- .../Exception/PoeDbCannotParseException.cs | 6 +- .../Extensions/GemDataExtensions.cs | 87 +++++++++++ .../Extensions/StringExtensions.cs | 10 ++ .../Repositories/IGemRepository.cs | 5 + .../Repositories/ILeagueRepository.cs | 8 + .../Repositories/IViewRepository.cs | 9 ++ src/Application/Services/AnalyticsService.cs | 42 +++-- src/Application/Services/GemService.cs | 44 +++--- src/Application/Services/IAnalyticsService.cs | 2 +- .../Services/IApplicationDbContext.cs | 20 --- .../Services/IApplicationDbContextFactory.cs | 6 - src/Application/Services/IDataFetchService.cs | 4 +- src/Application/Services/IGemService.cs | 5 +- src/Application/Services/ILeagueService.cs | 7 +- src/Application/Services/InitService.cs | 145 ------------------ src/Application/Services/LeagueService.cs | 21 +-- src/Application/Util/ExtensionMethods.cs | 4 +- src/Domain/Domain.csproj | 7 +- src/Domain/Entity/Abstract/Entity.cs | 12 +- src/Domain/Entity/Analytics/View.cs | 14 +- src/Domain/Entity/Currency.cs | 15 +- src/Domain/Entity/Gem/AlternateQuality.cs | 13 +- src/Domain/Entity/Gem/Case.cs | 13 +- src/Domain/Entity/Gem/GemData.cs | 84 ++-------- src/Domain/Entity/Gem/GemTradeData.cs | 27 ++-- src/Domain/Entity/League.cs | 22 +-- src/Domain/Entity/Stats/CurrencyResult.cs | 11 +- src/Domain/Entity/Stats/Result.cs | 18 ++- src/Domain/Entity/TempleCost.cs | 17 +- src/Domain/QueryParameters/GemDataQuery.cs | 13 -- src/Domain/QueryParameters/GemType.cs | 10 -- src/Domain/QueryParameters/Page.cs | 8 - src/Domain/QueryParameters/PageRequest.cs | 13 -- src/Domain/QueryParameters/Sort.cs | 11 -- src/Domain/Util/ExtensionMethods.cs | 11 +- .../BackgroundJobs/BackgroundJob.cs | 33 ++++ .../BackgroundJobs/FetchLeagueJob.cs | 33 ++++ .../BackgroundJobs/FetchPriceDataJob.cs | 80 ++++++++++ .../BackgroundJobs/LogAnalyticsJob.cs | 19 +++ .../ApplicationDbContext.cs | 19 +-- .../Configuration/EntityConfiguration.cs | 35 +++++ .../Configuration/TempleCostConfiguration.cs | 18 +++ .../MigrateDatabaseExtension.cs | 10 +- .../Exceptions/DbConcurrencyException.cs | 8 + src/Infrastructure/Infrastructure.csproj | 4 + .../Migrations/20230216010517_1.0.Designer.cs | 1 + .../Migrations/20230309081719_2.0.Designer.cs | 1 + .../Migrations/20230419193237_1.1.Designer.cs | 1 + .../Migrations/20230420132741_1.2.Designer.cs | 1 + .../ApplicationDbContextModelSnapshot.cs | 1 + .../Repositories/CurrencyRepository.cs | 22 +++ ...ices.cs => ServiceCollectionExtensions.cs} | 43 ++++-- .../Services/ApplicationDbContextFactory.cs | 16 -- .../Services/DataFetchService.cs | 13 +- .../Services/FetchDtos/CurrencyPriceData.cs | 2 +- .../Services/FetchDtos/GemPriceData.cs | 2 +- .../FetchDtos/PoeNinjaCurrencyData.cs | 4 +- .../FetchDtos/PoeNinjaCurrencyDetails.cs | 2 +- .../Services/FetchDtos/PoeNinjaGemData.cs | 4 +- .../Services/FetchDtos/TradeEntry.cs | 2 +- .../Services/FetchDtos/TradeEntryListing.cs | 2 +- .../FetchDtos/TradeEntryListingPrice.cs | 6 +- .../Services/FetchDtos/TradeEntryResult.cs | 2 +- .../Services/FetchDtos/TradeResults.cs | 2 +- src/Infrastructure/Util/ExtensionMethods.cs | 13 +- src/Infrastructure/Util/PoeToolUrls.cs | 2 +- src/Shared/QueryParameters/GemDataQuery.cs | 14 ++ src/Shared/QueryParameters/GemType.cs | 11 ++ src/Shared/QueryParameters/Page.cs | 9 ++ src/Shared/QueryParameters/PageRequest.cs | 14 ++ src/Shared/QueryParameters/Sort.cs | 12 ++ src/Shared/Shared.csproj | 7 + src/Web/Pages/GamblingHelper.razor.cs | 5 +- .../Implementations/CurrencyService.cs | 2 +- .../Services/Implementations/GemService.cs | 4 +- .../Services/Implementations/LeagueService.cs | 2 +- .../Implementations/TempleCostService.cs | 2 +- .../Services/Interfaces/ICurrencyService.cs | 2 +- src/Web/Services/Interfaces/IGemService.cs | 3 +- src/Web/Services/Interfaces/ILeagueService.cs | 2 +- .../Services/Interfaces/ITempleCostService.cs | 2 +- src/Web/Shared/Components/Filter.razor.cs | 3 +- src/Web/Shared/Components/GemStats.razor.cs | 5 +- src/Web/Shared/Model/FilterValues.cs | 3 +- src/Web/Util/ExtensionMethods.cs | 13 +- .../Services/GemServiceTest.cs | 8 +- .../Services/InitServiceTest.cs | 7 +- .../Services/LeagueServiceTest.cs | 8 +- test/Domain.Test/Entity/GemDataTest.cs | 8 +- test/Domain.Test/Entity/TempleCostTest.cs | 2 +- .../QueryParameters/PageRequestTest.cs | 3 - test/Domain.Test/Util/ExtensionMethodsTest.cs | 4 +- .../FetchDtos/PoeNinjaCurrencyDataTest.cs | 2 +- .../Services/FetchDtos/PoeNinjaGemDataTest.cs | 2 +- .../FetchDtos/TradeEntryListingPriceTest.cs | 6 +- .../Util/ExtensionMethodsTest.cs | 8 +- .../Web.Test/Shared/Model/FilterValuesTest.cs | 4 +- test/Web.Test/Util/ExtensionFunctionsTest.cs | 1 - 121 files changed, 811 insertions(+), 660 deletions(-) create mode 100644 Directory.Build.props create mode 100644 src/Api/AnalyticsMiddleware.cs rename src/{Domain => Application}/Exception/Abstract/HttpException.cs (75%) rename src/{Domain => Application}/Exception/Abstract/InternalServerErrorException.cs (65%) rename src/{Domain => Application}/Exception/Abstract/NotFoundException.cs (61%) rename src/{Domain => Application}/Exception/Abstract/PoeGamblingHelperException.cs (80%) rename src/{Domain => Application}/Exception/ApiDownException.cs (84%) rename src/{Domain => Application}/Exception/Body/ExceptionId.cs (94%) rename src/{Domain => Application}/Exception/Body/ExceptionType.cs (51%) rename src/{Domain => Application}/Exception/Body/PoeGamblingHelperExceptionBody.cs (88%) rename src/{Domain => Application}/Exception/NoLeagueDataException.cs (69%) rename src/{Domain => Application}/Exception/NoTempleDataException.cs (69%) rename src/{Domain => Application}/Exception/PoeDbCannotParseException.cs (76%) create mode 100644 src/Application/Extensions/GemDataExtensions.cs create mode 100644 src/Application/Extensions/StringExtensions.cs create mode 100644 src/Application/Repositories/IGemRepository.cs create mode 100644 src/Application/Repositories/ILeagueRepository.cs create mode 100644 src/Application/Repositories/IViewRepository.cs delete mode 100644 src/Application/Services/IApplicationDbContext.cs delete mode 100644 src/Application/Services/IApplicationDbContextFactory.cs delete mode 100644 src/Application/Services/InitService.cs delete mode 100644 src/Domain/QueryParameters/GemDataQuery.cs delete mode 100644 src/Domain/QueryParameters/GemType.cs delete mode 100644 src/Domain/QueryParameters/Page.cs delete mode 100644 src/Domain/QueryParameters/PageRequest.cs delete mode 100644 src/Domain/QueryParameters/Sort.cs create mode 100644 src/Infrastructure/BackgroundJobs/BackgroundJob.cs create mode 100644 src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs create mode 100644 src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs create mode 100644 src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs rename src/Infrastructure/{Services => Database}/ApplicationDbContext.cs (64%) create mode 100644 src/Infrastructure/Database/Configuration/EntityConfiguration.cs create mode 100644 src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs rename src/Infrastructure/{ => Database}/MigrateDatabaseExtension.cs (62%) create mode 100644 src/Infrastructure/Exceptions/DbConcurrencyException.cs create mode 100644 src/Infrastructure/Repositories/CurrencyRepository.cs rename src/Infrastructure/{ConfigureServices.cs => ServiceCollectionExtensions.cs} (62%) delete mode 100644 src/Infrastructure/Services/ApplicationDbContextFactory.cs create mode 100644 src/Shared/QueryParameters/GemDataQuery.cs create mode 100644 src/Shared/QueryParameters/GemType.cs create mode 100644 src/Shared/QueryParameters/Page.cs create mode 100644 src/Shared/QueryParameters/PageRequest.cs create mode 100644 src/Shared/QueryParameters/Sort.cs create mode 100644 src/Shared/Shared.csproj diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..bd2f7dc5 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,7 @@ + + + PoEGamblingHelper.$(MSBuildProjectName) + enable + enable + + \ No newline at end of file diff --git a/PoEGamblingHelper.sln b/PoEGamblingHelper.sln index 7678fc08..cbe364bb 100644 --- a/PoEGamblingHelper.sln +++ b/PoEGamblingHelper.sln @@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Test", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Test", "test\Web.Test\Web.Test.csproj", "{72665095-42B7-4E4A-8019-1B6EF259C31B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{518594C7-55A5-4D4D-B771-01457BA8B17A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,5 +62,9 @@ Global {72665095-42B7-4E4A-8019-1B6EF259C31B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.Build.0 = Release|Any CPU + {518594C7-55A5-4D4D-B771-01457BA8B17A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {518594C7-55A5-4D4D-B771-01457BA8B17A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {518594C7-55A5-4D4D-B771-01457BA8B17A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {518594C7-55A5-4D4D-B771-01457BA8B17A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Api/AnalyticsMiddleware.cs b/src/Api/AnalyticsMiddleware.cs new file mode 100644 index 00000000..1ba3b259 --- /dev/null +++ b/src/Api/AnalyticsMiddleware.cs @@ -0,0 +1,26 @@ +using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Application.Services; + +namespace PoEGamblingHelper.Api; + +public class AnalyticsMiddleware +{ + private readonly RequestDelegate _next; + + public AnalyticsMiddleware(RequestDelegate next) { _next = next; } + + // IMessageWriter is injected into InvokeAsync + public async Task InvokeAsync(HttpContext httpContext, IAnalyticsService analyticsService) + { + await analyticsService.AddView(httpContext.Request.GetRealIpAddress()); + await _next(httpContext); + } +} + +public static class AnalyticsMiddlewareExtensions +{ + public static IApplicationBuilder UseAnalytics(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/src/Api/Controllers/ApiControllerBase.cs b/src/Api/Controllers/ApiControllerBase.cs index 47735b63..72856747 100644 --- a/src/Api/Controllers/ApiControllerBase.cs +++ b/src/Api/Controllers/ApiControllerBase.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; [ApiController] [Route("[controller]")] diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 43d1886a..351bd998 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -1,20 +1,21 @@ -using Application.Services; -using Domain.Entity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Repositories; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public class CurrencyController : ApiControllerBase { private readonly IAnalyticsService _analyticsService; - private readonly IApplicationDbContextFactory _applicationDbContextFactory; + private readonly CurrencyRepository _currencyRepository; - public CurrencyController(IApplicationDbContextFactory applicationDbContextFactory, - IAnalyticsService analyticsService) + public CurrencyController(IAnalyticsService analyticsService, + CurrencyRepository currencyRepository) { - _applicationDbContextFactory = applicationDbContextFactory; _analyticsService = analyticsService; + _currencyRepository = currencyRepository; } [HttpGet] @@ -22,8 +23,6 @@ public CurrencyController(IApplicationDbContextFactory applicationDbContextFacto public async IAsyncEnumerable GetAll() { await _analyticsService.AddView(Request.GetRealIpAddress()); - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - await foreach (var item in applicationDbContext.Currency.AsAsyncEnumerable().ConfigureAwait(false)) - yield return item; + return _currencyRepository.GetAll(); } } \ No newline at end of file diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index 85db35ff..5519f13a 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -1,8 +1,8 @@ -using Application.Services; -using Domain.Entity.Gem; -using Domain.QueryParameters; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Api.Controllers; diff --git a/src/Api/Controllers/HttpUtilFunctions.cs b/src/Api/Controllers/HttpUtilFunctions.cs index e72117f5..64248671 100644 --- a/src/Api/Controllers/HttpUtilFunctions.cs +++ b/src/Api/Controllers/HttpUtilFunctions.cs @@ -1,4 +1,4 @@ -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public static class HttpUtilFunctions { diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index 4e3fd4b3..c79dd37a 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -1,7 +1,8 @@ -using Application.Services; -using Domain.Entity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; namespace Api.Controllers; @@ -11,7 +12,8 @@ public class LeagueController : ApiControllerBase private readonly IApplicationDbContextFactory _applicationDbContextFactory; private readonly ILeagueService _leagueService; - public LeagueController(ILeagueService leagueService, IApplicationDbContextFactory applicationDbContextFactory, + public LeagueController(ILeagueService leagueService, + IApplicationDbContextFactory applicationDbContextFactory, IAnalyticsService analyticsService) { _leagueService = leagueService; diff --git a/src/Api/Controllers/StatsController.cs b/src/Api/Controllers/StatsController.cs index 0579783d..3328811c 100644 --- a/src/Api/Controllers/StatsController.cs +++ b/src/Api/Controllers/StatsController.cs @@ -1,10 +1,6 @@ -using Application.Services; -using Domain.Entity; -using Domain.Entity.Stats; -using Domain.Exception; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.OutputCaching; -using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Domain.Entity.Stats; namespace Api.Controllers; diff --git a/src/Api/Controllers/TempleController.cs b/src/Api/Controllers/TempleController.cs index 5651adee..2e0b0da1 100644 --- a/src/Api/Controllers/TempleController.cs +++ b/src/Api/Controllers/TempleController.cs @@ -1,8 +1,9 @@ -using Application.Services; -using Domain.Entity; -using Domain.Exception; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; namespace Api.Controllers; diff --git a/src/Api/Filters/HttpResponseExceptionFilter.cs b/src/Api/Filters/HttpResponseExceptionFilter.cs index 197485c4..522fa9ff 100644 --- a/src/Api/Filters/HttpResponseExceptionFilter.cs +++ b/src/Api/Filters/HttpResponseExceptionFilter.cs @@ -1,6 +1,6 @@ -using Domain.Exception.Abstract; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using PoEGamblingHelper.Application.Exception.Abstract; namespace Api.Filters; diff --git a/src/Api/Program.cs b/src/Api/Program.cs index c515d4ea..9928bb41 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,9 +1,11 @@ using System.Globalization; using Api; using Api.Filters; -using Application.Services; -using Infrastructure; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Infrastructure; +using PoEGamblingHelper.Infrastructure.Database; #if DEBUG Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); @@ -50,4 +52,6 @@ app.UseOutputCache(); app.MapControllers(); app.MigrateDatabase(); +app.UseAnalytics(); + app.Run(); \ No newline at end of file diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index a3ecf62c..de3fcba9 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -2,8 +2,6 @@ net7.0 - enable - enable @@ -11,11 +9,6 @@ - - - - - - + diff --git a/src/Domain/Exception/Abstract/HttpException.cs b/src/Application/Exception/Abstract/HttpException.cs similarity index 75% rename from src/Domain/Exception/Abstract/HttpException.cs rename to src/Application/Exception/Abstract/HttpException.cs index a826db50..a4a41af4 100644 --- a/src/Domain/Exception/Abstract/HttpException.cs +++ b/src/Application/Exception/Abstract/HttpException.cs @@ -1,6 +1,6 @@ -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception.Abstract; +namespace PoEGamblingHelper.Application.Exception.Abstract; public abstract class HttpException : PoeGamblingHelperException { diff --git a/src/Domain/Exception/Abstract/InternalServerErrorException.cs b/src/Application/Exception/Abstract/InternalServerErrorException.cs similarity index 65% rename from src/Domain/Exception/Abstract/InternalServerErrorException.cs rename to src/Application/Exception/Abstract/InternalServerErrorException.cs index 1ae974e7..d0057f1c 100644 --- a/src/Domain/Exception/Abstract/InternalServerErrorException.cs +++ b/src/Application/Exception/Abstract/InternalServerErrorException.cs @@ -1,6 +1,6 @@ -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception.Abstract; +namespace PoEGamblingHelper.Application.Exception.Abstract; public abstract class InternalServerErrorException : HttpException { diff --git a/src/Domain/Exception/Abstract/NotFoundException.cs b/src/Application/Exception/Abstract/NotFoundException.cs similarity index 61% rename from src/Domain/Exception/Abstract/NotFoundException.cs rename to src/Application/Exception/Abstract/NotFoundException.cs index 336d7284..ede6dd44 100644 --- a/src/Domain/Exception/Abstract/NotFoundException.cs +++ b/src/Application/Exception/Abstract/NotFoundException.cs @@ -1,6 +1,6 @@ -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception.Abstract; +namespace PoEGamblingHelper.Application.Exception.Abstract; public abstract class NotFoundException : HttpException { diff --git a/src/Domain/Exception/Abstract/PoeGamblingHelperException.cs b/src/Application/Exception/Abstract/PoeGamblingHelperException.cs similarity index 80% rename from src/Domain/Exception/Abstract/PoeGamblingHelperException.cs rename to src/Application/Exception/Abstract/PoeGamblingHelperException.cs index bbb5b401..7fe1bb56 100644 --- a/src/Domain/Exception/Abstract/PoeGamblingHelperException.cs +++ b/src/Application/Exception/Abstract/PoeGamblingHelperException.cs @@ -1,4 +1,4 @@ -namespace Domain.Exception.Abstract; +namespace PoEGamblingHelper.Application.Exception.Abstract; public abstract class PoeGamblingHelperException : System.Exception { diff --git a/src/Domain/Exception/ApiDownException.cs b/src/Application/Exception/ApiDownException.cs similarity index 84% rename from src/Domain/Exception/ApiDownException.cs rename to src/Application/Exception/ApiDownException.cs index cf178261..189122dc 100644 --- a/src/Domain/Exception/ApiDownException.cs +++ b/src/Application/Exception/ApiDownException.cs @@ -1,7 +1,7 @@ -using Domain.Exception.Abstract; -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception; +namespace PoEGamblingHelper.Application.Exception; public class ApiDownException : InternalServerErrorException { diff --git a/src/Domain/Exception/Body/ExceptionId.cs b/src/Application/Exception/Body/ExceptionId.cs similarity index 94% rename from src/Domain/Exception/Body/ExceptionId.cs rename to src/Application/Exception/Body/ExceptionId.cs index 02f01d66..89a13779 100644 --- a/src/Domain/Exception/Body/ExceptionId.cs +++ b/src/Application/Exception/Body/ExceptionId.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Domain.Exception.Body; +namespace PoEGamblingHelper.Application.Exception.Body; public static class ExceptionIdExtensions { diff --git a/src/Domain/Exception/Body/ExceptionType.cs b/src/Application/Exception/Body/ExceptionType.cs similarity index 51% rename from src/Domain/Exception/Body/ExceptionType.cs rename to src/Application/Exception/Body/ExceptionType.cs index 38e92d60..f74e0400 100644 --- a/src/Domain/Exception/Body/ExceptionType.cs +++ b/src/Application/Exception/Body/ExceptionType.cs @@ -1,4 +1,4 @@ -namespace Domain.Exception.Body; +namespace PoEGamblingHelper.Application.Exception.Body; public enum ExceptionType { diff --git a/src/Domain/Exception/Body/PoeGamblingHelperExceptionBody.cs b/src/Application/Exception/Body/PoeGamblingHelperExceptionBody.cs similarity index 88% rename from src/Domain/Exception/Body/PoeGamblingHelperExceptionBody.cs rename to src/Application/Exception/Body/PoeGamblingHelperExceptionBody.cs index 79d67296..168f3648 100644 --- a/src/Domain/Exception/Body/PoeGamblingHelperExceptionBody.cs +++ b/src/Application/Exception/Body/PoeGamblingHelperExceptionBody.cs @@ -1,4 +1,4 @@ -namespace Domain.Exception.Body; +namespace PoEGamblingHelper.Application.Exception.Body; public record PoeGamblingHelperExceptionBody(ExceptionType Type, ExceptionId Id, diff --git a/src/Domain/Exception/NoLeagueDataException.cs b/src/Application/Exception/NoLeagueDataException.cs similarity index 69% rename from src/Domain/Exception/NoLeagueDataException.cs rename to src/Application/Exception/NoLeagueDataException.cs index bd966e3f..4a19c349 100644 --- a/src/Domain/Exception/NoLeagueDataException.cs +++ b/src/Application/Exception/NoLeagueDataException.cs @@ -1,7 +1,7 @@ -using Domain.Exception.Abstract; -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception; +namespace PoEGamblingHelper.Application.Exception; public class NoLeagueDataException : NotFoundException { diff --git a/src/Domain/Exception/NoTempleDataException.cs b/src/Application/Exception/NoTempleDataException.cs similarity index 69% rename from src/Domain/Exception/NoTempleDataException.cs rename to src/Application/Exception/NoTempleDataException.cs index c3e18bdd..2567b4df 100644 --- a/src/Domain/Exception/NoTempleDataException.cs +++ b/src/Application/Exception/NoTempleDataException.cs @@ -1,7 +1,7 @@ -using Domain.Exception.Abstract; -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception; +namespace PoEGamblingHelper.Application.Exception; public class NoTempleDataException : NotFoundException { diff --git a/src/Domain/Exception/PoeDbCannotParseException.cs b/src/Application/Exception/PoeDbCannotParseException.cs similarity index 76% rename from src/Domain/Exception/PoeDbCannotParseException.cs rename to src/Application/Exception/PoeDbCannotParseException.cs index 2ebce3b3..95d66110 100644 --- a/src/Domain/Exception/PoeDbCannotParseException.cs +++ b/src/Application/Exception/PoeDbCannotParseException.cs @@ -1,7 +1,7 @@ -using Domain.Exception.Abstract; -using Domain.Exception.Body; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Exception.Body; -namespace Domain.Exception; +namespace PoEGamblingHelper.Application.Exception; public class PoeDbCannotParseException : InternalServerErrorException { diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs new file mode 100644 index 00000000..2dbeec5e --- /dev/null +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -0,0 +1,87 @@ +using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Domain.Util; + +namespace PoEGamblingHelper.Application.Extensions; + +public static class GemDataExtensions +{ + public static decimal AvgProfitPerTry(this GemData gemData, + decimal? rawCost = null, + decimal? worstCaseValue = null, + decimal? middleCaseValue = null, + decimal? bestCaseValue = null, + decimal templeCost = 0) + { + var worstCaseProfit = worstCaseValue is null + ? gemData.Profit(ResultCase.Worst, rawCost, templeCost) + : gemData.Profit((decimal)worstCaseValue, rawCost, templeCost); + var middleCaseProfit = middleCaseValue is null + ? gemData.Profit(ResultCase.Middle, rawCost, templeCost) + : gemData.Profit((decimal)middleCaseValue, rawCost, templeCost); + var bestCaseProfit = bestCaseValue is null + ? gemData.Profit(ResultCase.Best, rawCost, templeCost) + : gemData.Profit((decimal)bestCaseValue, rawCost, templeCost); + return (worstCaseProfit + 2 * middleCaseProfit + bestCaseProfit) / 4; + } + + public static decimal Profit(this GemData gemData, + ResultCase resultCase, + decimal? rawCost = null, + decimal templeCost = 0) + { + return gemData.Profit(gemData.Value(resultCase), rawCost, templeCost); + } + + public static decimal Profit(this GemData gemData, + decimal value, + decimal? rawCost = null, + decimal templeCost = 0) + { + return value - gemData.CostPerTry(rawCost, templeCost); + } + + public static decimal Value(this GemData gemData, ResultCase resultCase) + { + return gemData.ResultValue(gemData.MaxLevel() + resultCase.LevelModifier()); + } + + internal static decimal ResultValue(this GemData gemData, int level) + { + return gemData.Gems.Where(gem => gem.GemLevel == level && gem.Corrupted).MinBy(gem => gem.GemQuality) + ?.ChaosValue ?? 0m; + } + + public static int MaxLevel(this GemData gemData) + { + var isAwakened = gemData.Name.ToLowerInvariant().Contains("awakened"); + var isExceptional = gemData.IsExceptional(); + return isAwakened && isExceptional ? 4 : + isExceptional ? 3 : + isAwakened ? 5 : + 20; + } + + internal static bool IsExceptional(this GemData gemData) + { + var lowerName = gemData.Name.ToLowerInvariant(); + return lowerName.Contains("enhance") || lowerName.Contains("empower") || lowerName.Contains("enlighten"); + } + + public static decimal CostPerTry(this GemData gemData, + decimal? rawCost = null, + decimal templeCost = 0) + { + return (rawCost ?? gemData.RawCost()) + templeCost; + } + + public static decimal RawCost(this GemData gemData) + { + return gemData.Gems.Where(gem => gem.GemLevel == gemData.MaxLevel() && !gem.Corrupted) + .MinBy(gem => gem.GemQuality)?.ChaosValue ?? 0m; + } + + public static string ToString(this GemData gemData) + { + return $"[Id={gemData.Id}, Name={gemData.Name}, MaxLevel={gemData.MaxLevel()}]"; + } +} \ No newline at end of file diff --git a/src/Application/Extensions/StringExtensions.cs b/src/Application/Extensions/StringExtensions.cs new file mode 100644 index 00000000..521f6a0b --- /dev/null +++ b/src/Application/Extensions/StringExtensions.cs @@ -0,0 +1,10 @@ +namespace PoEGamblingHelper.Application.Extensions; + +public static class StringExtensions +{ + public static bool EqualsIgnoreCase(this string? a, string? b) + { + return a?.Equals(b, StringComparison.InvariantCultureIgnoreCase) + ?? b is null; + } +} \ No newline at end of file diff --git a/src/Application/Repositories/IGemRepository.cs b/src/Application/Repositories/IGemRepository.cs new file mode 100644 index 00000000..85219006 --- /dev/null +++ b/src/Application/Repositories/IGemRepository.cs @@ -0,0 +1,5 @@ +namespace PoEGamblingHelper.Application.Repositories; + +public interface IGemRepository +{ +} \ No newline at end of file diff --git a/src/Application/Repositories/ILeagueRepository.cs b/src/Application/Repositories/ILeagueRepository.cs new file mode 100644 index 00000000..d716b550 --- /dev/null +++ b/src/Application/Repositories/ILeagueRepository.cs @@ -0,0 +1,8 @@ +using PoEGamblingHelper.Domain.Entity; + +namespace PoEGamblingHelper.Application.Repositories; + +public interface ILeagueRepository +{ + League GetByStartDateAfter(DateTime dateTime); +} \ No newline at end of file diff --git a/src/Application/Repositories/IViewRepository.cs b/src/Application/Repositories/IViewRepository.cs new file mode 100644 index 00000000..2d9720c9 --- /dev/null +++ b/src/Application/Repositories/IViewRepository.cs @@ -0,0 +1,9 @@ +using PoEGamblingHelper.Domain.Entity.Analytics; + +namespace PoEGamblingHelper.Application.Repositories; + +public interface IViewRepository +{ + Task AddAsync(View view); + Task LogViewsAsync(DateOnly date); +} \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index 2ed5603b..f8dbe230 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -1,44 +1,40 @@ using System.Security.Cryptography; using System.Text; -using Domain.Entity.Analytics; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity.Analytics; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public class AnalyticsService : IAnalyticsService { - private readonly IApplicationDbContextFactory _applicationDbContextFactory; - private readonly ILogger _logger; + private readonly IViewRepository _viewRepository; - public AnalyticsService(IApplicationDbContextFactory applicationDbContextFactory, ILogger logger) - { - _applicationDbContextFactory = applicationDbContextFactory; - _logger = logger; - } + public AnalyticsService(IViewRepository viewRepository) { _viewRepository = viewRepository; } public async Task AddView(string? ipAddress) { if (ipAddress is null) return; var ipHash = SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); var today = DateOnly.FromDateTime(DateTime.UtcNow); - var view = new View { IpHash = ipHash, TimeStamp = today }; - using var ctx = _applicationDbContextFactory.CreateDbContext(); - if (await ctx.View.AsNoTracking().AnyAsync(v => v.IpHash.Equals(ipHash) && v.TimeStamp == today)) return; + var view = new View { IpHash = ipHash, TimeStamp = today.ToDateTime(TimeOnly.MinValue) }; + await _viewRepository.AddAsync(view); + // using var ctx = _applicationDbContextFactory.CreateDbContext(); + // if (await ctx.View.AsNoTracking().AnyAsync(v => v.IpHash.Equals(ipHash) && v.TimeStamp == today)) return; - await ctx.View.AddAsync(view); - await ctx.SaveChangesAsync(); + // await ctx.View.AddAsync(view); + // await ctx.SaveChangesAsync(); } public async Task LogYesterdaysViews() { var yesterday = DateOnly.FromDateTime(DateTime.UtcNow).AddDays(-1); - using var ctx = _applicationDbContextFactory.CreateDbContext(); - var viewCount = await ctx.View.Where(v => v.TimeStamp == yesterday).CountAsync(); - _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", - viewCount, yesterday.Day, yesterday.Month, yesterday.Year); - var views = await ctx.View.Where(v => v.TimeStamp <= yesterday).ToArrayAsync(); - ctx.View.RemoveRange(views); - await ctx.SaveChangesAsync(); + await _viewRepository.LogViewsAsync(yesterday); + // using var ctx = _applicationDbContextFactory.CreateDbContext(); + // var viewCount = await ctx.View.Where(v => v.TimeStamp == yesterday).CountAsync(); + // _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", + // viewCount, yesterday.Day, yesterday.Month, yesterday.Year); + // var views = await ctx.View.Where(v => v.TimeStamp <= yesterday).ToArrayAsync(); + // ctx.View.RemoveRange(views); + // await ctx.SaveChangesAsync(); } } \ No newline at end of file diff --git a/src/Application/Services/GemService.cs b/src/Application/Services/GemService.cs index 4642bab4..97fa44aa 100644 --- a/src/Application/Services/GemService.cs +++ b/src/Application/Services/GemService.cs @@ -1,20 +1,14 @@ using System.Diagnostics; using System.Text.RegularExpressions; -using Application.Util; -using Domain.Entity.Gem; -using Domain.QueryParameters; -using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public partial class GemService : IGemService { - private readonly IApplicationDbContextFactory _applicationDbContextFactory; - - public GemService(IApplicationDbContextFactory applicationDbContextFactory) - { - _applicationDbContextFactory = applicationDbContextFactory; - } + private readonly IGemRepository _gemRepository; + public GemService(IGemRepository gemRepository) { _gemRepository = gemRepository; } public async Task> GetAll(GemDataQuery? query, PageRequest page) { @@ -69,17 +63,17 @@ private GemData[] FilterGemData(GemDataQuery query) private static string PreFilterGemDataQuery(GemDataQuery query) { const string isAlternateQuality = """ - (LOWER("Name") LIKE 'anomalous%' - OR LOWER("Name") LIKE 'divergent%' - OR LOWER("Name") LIKE 'phantasmal%') - """; + (LOWER("Name") LIKE 'anomalous%' + OR LOWER("Name") LIKE 'divergent%' + OR LOWER("Name") LIKE 'phantasmal%') + """; const string isVaal = """LOWER("Name") LIKE 'vaal%' """; const string isExceptional = """ - (LOWER("Name") LIKE '%enhance%' - OR LOWER("Name") LIKE '%empower%' - OR LOWER("Name") LIKE '%enlighten%') - """; + (LOWER("Name") LIKE '%enhance%' + OR LOWER("Name") LIKE '%empower%' + OR LOWER("Name") LIKE '%enlighten%') + """; const string isAwakened = """LOWER("Name") LIKE 'awakened%' """; const string isSupport = """LOWER("Name") LIKE '%support' """; var isGemTypeMatching = query.GemType switch @@ -93,12 +87,12 @@ OR LOWER("Name") LIKE '%enlighten%') }; return $""" - SELECT * FROM "GemData" - WHERE LOWER("Name") LIKE '%{query.SearchText}%' - AND ({query.ShowAlternateQuality} OR NOT {isAlternateQuality}) - AND ({query.ShowVaal} OR NOT {isVaal}) - AND {isGemTypeMatching} - """; + SELECT * FROM "GemData" + WHERE LOWER("Name") LIKE '%{query.SearchText}%' + AND ({query.ShowAlternateQuality} OR NOT {isAlternateQuality}) + AND ({query.ShowVaal} OR NOT {isVaal}) + AND {isGemTypeMatching} + """; } private static bool PostFilterGemData(GemData gemData, GemDataQuery query, decimal averageTempleCost) diff --git a/src/Application/Services/IAnalyticsService.cs b/src/Application/Services/IAnalyticsService.cs index 16497071..92269c6f 100644 --- a/src/Application/Services/IAnalyticsService.cs +++ b/src/Application/Services/IAnalyticsService.cs @@ -1,4 +1,4 @@ -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public interface IAnalyticsService { diff --git a/src/Application/Services/IApplicationDbContext.cs b/src/Application/Services/IApplicationDbContext.cs deleted file mode 100644 index ab37c87a..00000000 --- a/src/Application/Services/IApplicationDbContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Domain.Entity; -using Domain.Entity.Analytics; -using Domain.Entity.Gem; -using Domain.Entity.Stats; -using Microsoft.EntityFrameworkCore; - -namespace Application.Services; - -public interface IApplicationDbContext : IDisposable -{ - DbSet Currency { get; } - DbSet League { get; } - DbSet TempleCost { get; } - DbSet GemData { get; } - DbSet GemTradeData { get; } - DbSet Result { get; } - DbSet View { get; } - Task SaveChangesAsync(); - void ClearTrackedEntities(); -} \ No newline at end of file diff --git a/src/Application/Services/IApplicationDbContextFactory.cs b/src/Application/Services/IApplicationDbContextFactory.cs deleted file mode 100644 index 762dc768..00000000 --- a/src/Application/Services/IApplicationDbContextFactory.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Application.Services; - -public interface IApplicationDbContextFactory -{ - IApplicationDbContext CreateDbContext(); -} \ No newline at end of file diff --git a/src/Application/Services/IDataFetchService.cs b/src/Application/Services/IDataFetchService.cs index fab62c72..96c255a5 100644 --- a/src/Application/Services/IDataFetchService.cs +++ b/src/Application/Services/IDataFetchService.cs @@ -1,6 +1,6 @@ -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public interface IDataFetchService { diff --git a/src/Application/Services/IGemService.cs b/src/Application/Services/IGemService.cs index 0f37da03..4eb47ba4 100644 --- a/src/Application/Services/IGemService.cs +++ b/src/Application/Services/IGemService.cs @@ -1,7 +1,6 @@ -using Domain.Entity.Gem; -using Domain.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public interface IGemService { diff --git a/src/Application/Services/ILeagueService.cs b/src/Application/Services/ILeagueService.cs index 9bef9422..c63ed91f 100644 --- a/src/Application/Services/ILeagueService.cs +++ b/src/Application/Services/ILeagueService.cs @@ -1,9 +1,8 @@ -using Domain.Entity; -using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Domain.Entity; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public interface ILeagueService { - League GetCurrentLeague(DbSet leagues); + League GetCurrentLeague(); } \ No newline at end of file diff --git a/src/Application/Services/InitService.cs b/src/Application/Services/InitService.cs deleted file mode 100644 index 78f32be7..00000000 --- a/src/Application/Services/InitService.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System.Runtime.CompilerServices; -using Domain.Entity; -using Domain.Exception; -using Domain.Exception.Abstract; -using Microsoft.AspNetCore.OutputCaching; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Timer = System.Timers.Timer; - -[assembly: InternalsVisibleTo("Application.Test")] - -namespace Application.Services; - -public class InitService : IHostedService -{ - private readonly IAnalyticsService _analyticsService; - private readonly IApplicationDbContextFactory _applicationDbContextFactory; - private readonly IOutputCacheStore _cache; - private readonly string _cacheTag; - private readonly IDataFetchService _dataFetchService; - private readonly TimeSpan _fetchInterval; - private readonly ILeagueService _leagueService; - private readonly ILogger _logger; - private Timer? _fetchLeagueTimer; - private Timer? _fetchPriceDataTimer; - private Timer? _logAnalyticsTimer; - - public InitService(ILogger logger, - IDataFetchService dataFetchService, - IOutputCacheStore cache, - TimeSpan fetchInterval, - string cacheTag, - IApplicationDbContextFactory applicationDbContextFactory, - ILeagueService leagueService, - IAnalyticsService analyticsService) - { - _logger = logger; - _dataFetchService = dataFetchService; - _cache = cache; - _fetchInterval = fetchInterval; - _cacheTag = cacheTag; - _leagueService = leagueService; - _analyticsService = analyticsService; - _applicationDbContextFactory = applicationDbContextFactory; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _fetchPriceDataTimer?.Dispose(); - _fetchLeagueTimer?.Dispose(); - return Task.CompletedTask; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("Starting initialization..."); - - await FetchCurrentLeague(); - await FetchPriceData(); - - _logger.LogInformation("Initialization finished"); - - _logger.LogInformation("Starting fetch timers..."); - - _fetchLeagueTimer = new Timer(TimeSpan.FromHours(6)); - _fetchLeagueTimer.Elapsed += async (_, _) => await FetchCurrentLeague(); - _fetchLeagueTimer.AutoReset = true; - _fetchLeagueTimer.Start(); - - _fetchPriceDataTimer = new Timer(_fetchInterval); - _fetchPriceDataTimer.Elapsed += async (_, _) => await FetchPriceData(); - _fetchPriceDataTimer.AutoReset = true; - _fetchPriceDataTimer.Start(); - - _logAnalyticsTimer = new Timer(TimeSpan.FromDays(1)); - _logAnalyticsTimer.Elapsed += async (_, _) => await _analyticsService.LogYesterdaysViews(); - _logAnalyticsTimer.AutoReset = true; - var timeUntilOneAm = DateTime.UtcNow.AddDays(1).Date.AddHours(1).Subtract(DateTime.UtcNow); - var delay = new Timer(timeUntilOneAm); - delay.AutoReset = false; - delay.Elapsed += (_, _) => _logAnalyticsTimer.Start(); - delay.Start(); - - _logger.LogInformation("Fetch timers started"); - } - - internal async Task FetchCurrentLeague() - { - try - { - await _dataFetchService.FetchCurrentLeague(); - } - catch (PoeGamblingHelperException e) - { - _logger.LogError("{Exception}", e); - } - } - - internal async Task FetchPriceData() - { - League league; - using (var context = _applicationDbContextFactory.CreateDbContext()) - { - try - { - league = _leagueService.GetCurrentLeague(context.League); - } - catch (NoLeagueDataException e) - { - _logger.LogError("Could not Fetch Price Data, because League could not be get: {Exception}", e); - return; - } - } - - try - { - await _dataFetchService.FetchCurrencyData(league); - } - catch (PoeGamblingHelperException e) - { - _logger.LogError("Could not Fetch CurrencyData: {Exception}", e); - } - - try - { - await _dataFetchService.FetchTemplePriceData(league); - } - catch (PoeGamblingHelperException e) - { - _logger.LogError("Could not Fetch TemplePriceData: {Exception}", e); - } - - try - { - await _dataFetchService.FetchGemPriceData(league); - } - catch (PoeGamblingHelperException e) - { - _logger.LogError("Could not Fetch GemPriceData: {Exception}", e); - } - - await _cache.EvictByTagAsync(_cacheTag, new CancellationToken()); - _logger.LogDebug("Cache cleared"); - } -} \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs index 1182e755..27360155 100644 --- a/src/Application/Services/LeagueService.cs +++ b/src/Application/Services/LeagueService.cs @@ -1,17 +1,20 @@ -using Domain.Entity; -using Domain.Exception; -using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity; -namespace Application.Services; +namespace PoEGamblingHelper.Application.Services; public class LeagueService : ILeagueService { - public League GetCurrentLeague(DbSet leagues) + private readonly ILeagueRepository _leagueRepository; + public LeagueService(ILeagueRepository leagueRepository) { _leagueRepository = leagueRepository; } + + public League GetCurrentLeague() { var utcNow = DateTime.Today.ToUniversalTime(); - return leagues.Where(league => utcNow >= league.StartDate) - .OrderByDescending(league => league.StartDate) - .FirstOrDefault() - ?? throw new NoLeagueDataException(); + return _leagueRepository.GetByStartDateAfter(utcNow); + // return leagues.Where(league => utcNow >= league.StartDate) + // .OrderByDescending(league => league.StartDate) + // .FirstOrDefault() + // ?? throw new NoLeagueDataException(); } } \ No newline at end of file diff --git a/src/Application/Util/ExtensionMethods.cs b/src/Application/Util/ExtensionMethods.cs index 2489aee4..179f30a9 100644 --- a/src/Application/Util/ExtensionMethods.cs +++ b/src/Application/Util/ExtensionMethods.cs @@ -1,6 +1,4 @@ -using Domain.QueryParameters; - -namespace Application.Util; +namespace PoEGamblingHelper.Application.Util; public static class ExtensionMethods { diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index 6836c680..3718f955 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -1,9 +1,6 @@ - - net7.0 - enable - enable + netstandard2.1 + disable - diff --git a/src/Domain/Entity/Abstract/Entity.cs b/src/Domain/Entity/Abstract/Entity.cs index c9cf2254..443c39ba 100644 --- a/src/Domain/Entity/Abstract/Entity.cs +++ b/src/Domain/Entity/Abstract/Entity.cs @@ -1,9 +1,7 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Domain.Entity.Abstract; - -public abstract class Entity +namespace PoEGamblingHelper.Domain.Entity.Abstract { - [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public T Id { get; set; } = default!; + public abstract class Entity + { + public T Id { get; set; } = default!; + } } \ No newline at end of file diff --git a/src/Domain/Entity/Analytics/View.cs b/src/Domain/Entity/Analytics/View.cs index 9d56490e..67181e7a 100644 --- a/src/Domain/Entity/Analytics/View.cs +++ b/src/Domain/Entity/Analytics/View.cs @@ -1,9 +1,11 @@ -using Domain.Entity.Abstract; +using System; +using PoEGamblingHelper.Domain.Entity.Abstract; -namespace Domain.Entity.Analytics; - -public class View : Entity +namespace PoEGamblingHelper.Domain.Entity.Analytics { - public byte[] IpHash { get; set; } = null!; - public DateOnly TimeStamp { get; set; } + public class View : Entity + { + public byte[] IpHash { get; set; } = null!; + public DateTime TimeStamp { get; set; } + } } \ No newline at end of file diff --git a/src/Domain/Entity/Currency.cs b/src/Domain/Entity/Currency.cs index 5f51196e..9b812c6b 100644 --- a/src/Domain/Entity/Currency.cs +++ b/src/Domain/Entity/Currency.cs @@ -1,10 +1,11 @@ -using Domain.Entity.Abstract; +using PoEGamblingHelper.Domain.Entity.Abstract; -namespace Domain.Entity; - -public class Currency : Entity +namespace PoEGamblingHelper.Domain.Entity { - public string Name { get; set; } = string.Empty; - public decimal ChaosEquivalent { get; set; } - public string? Icon { get; set; } + public class Currency : Entity + { + public string Name { get; set; } = string.Empty; + public decimal ChaosEquivalent { get; set; } + public string? Icon { get; set; } + } } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/AlternateQuality.cs b/src/Domain/Entity/Gem/AlternateQuality.cs index 935f4419..14a0e073 100644 --- a/src/Domain/Entity/Gem/AlternateQuality.cs +++ b/src/Domain/Entity/Gem/AlternateQuality.cs @@ -1,10 +1,11 @@ // ReSharper disable UnusedMember.Global -namespace Domain.Entity.Gem; - -public enum AlternateQuality +namespace PoEGamblingHelper.Domain.Entity.Gem { - Anomalous, - Divergent, - Phantasmal + public enum AlternateQuality + { + Anomalous, + Divergent, + Phantasmal + } } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/Case.cs b/src/Domain/Entity/Gem/Case.cs index 86cb6a23..d25d0115 100644 --- a/src/Domain/Entity/Gem/Case.cs +++ b/src/Domain/Entity/Gem/Case.cs @@ -1,8 +1,9 @@ -namespace Domain.Entity.Gem; - -public enum ResultCase +namespace PoEGamblingHelper.Domain.Entity.Gem { - Worst, - Middle, - Best + public enum ResultCase + { + Worst, + Middle, + Best + } } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/GemData.cs b/src/Domain/Entity/Gem/GemData.cs index 14036b37..d430d0e0 100644 --- a/src/Domain/Entity/Gem/GemData.cs +++ b/src/Domain/Entity/Gem/GemData.cs @@ -1,82 +1,16 @@ -using System.Runtime.CompilerServices; -using Domain.Entity.Abstract; -using Domain.Util; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using PoEGamblingHelper.Domain.Entity.Abstract; [assembly: InternalsVisibleTo("Domain.Test")] -namespace Domain.Entity.Gem; - -public class GemData : Entity +namespace PoEGamblingHelper.Domain.Entity.Gem { - public string Name { get; set; } = string.Empty; - public string Icon { get; set; } = string.Empty; - public ICollection Gems { get; set; } = new List(); - - public override string ToString() { return $"[Id={Id}, Name={Name}, MaxLevel={MaxLevel()}]"; } - - #region Calculations - - public decimal AvgProfitPerTry(decimal? rawCost = null, - decimal? worstCaseValue = null, - decimal? middleCaseValue = null, - decimal? bestCaseValue = null, - decimal templeCost = 0) - { - var worstCaseProfit = worstCaseValue is null - ? Profit(ResultCase.Worst, rawCost, templeCost) - : Profit((decimal)worstCaseValue, rawCost, templeCost); - var middleCaseProfit = middleCaseValue is null - ? Profit(ResultCase.Middle, rawCost, templeCost) - : Profit((decimal)middleCaseValue, rawCost, templeCost); - var bestCaseProfit = bestCaseValue is null - ? Profit(ResultCase.Best, rawCost, templeCost) - : Profit((decimal)bestCaseValue, rawCost, templeCost); - return (worstCaseProfit + 2 * middleCaseProfit + bestCaseProfit) / 4; - } - - public decimal Profit(ResultCase resultCase, decimal? rawCost = null, decimal templeCost = 0) - { - return Profit(Value(resultCase), rawCost, templeCost); - } - - public decimal Profit(decimal value, decimal? rawCost = null, decimal templeCost = 0) - { - return value - CostPerTry(rawCost, templeCost); - } - - public decimal Value(ResultCase resultCase) { return ResultValue(MaxLevel() + resultCase.LevelModifier()); } - - internal decimal ResultValue(int level) + public class GemData : Entity { - return Gems.Where(gem => gem.GemLevel == level && gem.Corrupted).MinBy(gem => gem.GemQuality)?.ChaosValue ?? 0m; + public string Name { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public ICollection Gems { get; set; } = new List(); } - - public int MaxLevel() - { - var isAwakened = Name.ToLowerInvariant().Contains("awakened"); - var isExceptional = IsExceptional(); - return isAwakened && isExceptional ? 4 : - isExceptional ? 3 : - isAwakened ? 5 : - 20; - } - - internal bool IsExceptional() - { - var lowerName = Name.ToLowerInvariant(); - return lowerName.Contains("enhance") || lowerName.Contains("empower") || lowerName.Contains("enlighten"); - } - - public decimal CostPerTry(decimal? rawCost = null, decimal templeCost = 0) - { - return (rawCost ?? RawCost()) + templeCost; - } - - public decimal RawCost() - { - return Gems.Where(gem => gem.GemLevel == MaxLevel() && !gem.Corrupted) - .MinBy(gem => gem.GemQuality)?.ChaosValue ?? 0m; - } - - #endregion } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/GemTradeData.cs b/src/Domain/Entity/Gem/GemTradeData.cs index 628d45e4..43bbc66b 100644 --- a/src/Domain/Entity/Gem/GemTradeData.cs +++ b/src/Domain/Entity/Gem/GemTradeData.cs @@ -1,16 +1,17 @@ -using Domain.Entity.Abstract; +using PoEGamblingHelper.Domain.Entity.Abstract; -namespace Domain.Entity.Gem; - -public class GemTradeData : Entity +namespace PoEGamblingHelper.Domain.Entity.Gem { - public string Name { get; set; } = string.Empty; - public int GemLevel { get; set; } - public int GemQuality { get; set; } - public bool Corrupted { get; set; } - public string DetailsId { get; set; } = null!; - public decimal ChaosValue { get; set; } - public decimal ExaltedValue { get; set; } - public decimal DivineValue { get; set; } - public int ListingCount { get; set; } + public class GemTradeData : Entity + { + public string Name { get; set; } = string.Empty; + public int GemLevel { get; set; } + public int GemQuality { get; set; } + public bool Corrupted { get; set; } + public string DetailsId { get; set; } = null!; + public decimal ChaosValue { get; set; } + public decimal ExaltedValue { get; set; } + public decimal DivineValue { get; set; } + public int ListingCount { get; set; } + } } \ No newline at end of file diff --git a/src/Domain/Entity/League.cs b/src/Domain/Entity/League.cs index 3f6dd67c..4bb1c2c6 100644 --- a/src/Domain/Entity/League.cs +++ b/src/Domain/Entity/League.cs @@ -1,15 +1,17 @@ -using Domain.Entity.Abstract; +using System; +using PoEGamblingHelper.Domain.Entity.Abstract; -namespace Domain.Entity; - -public class League : Entity +namespace PoEGamblingHelper.Domain.Entity { - public string Name { get; set; } = null!; - public DateTime StartDate { get; set; } - public string Version { get; set; } = null!; - - public override string ToString() + public class League : Entity { - return $"[id={Id}, Version={Version}, Name={Name}, StartDate={StartDate.ToLongDateString()}]"; + public string Name { get; set; } = null!; + public DateTime StartDate { get; set; } + public string Version { get; set; } = null!; + + public override string ToString() + { + return $"[id={Id}, Version={Version}, Name={Name}, StartDate={StartDate.ToLongDateString()}]"; + } } } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/CurrencyResult.cs b/src/Domain/Entity/Stats/CurrencyResult.cs index 9f325256..74ca3b9b 100644 --- a/src/Domain/Entity/Stats/CurrencyResult.cs +++ b/src/Domain/Entity/Stats/CurrencyResult.cs @@ -1,7 +1,10 @@ -namespace Domain.Entity.Stats; +using PoEGamblingHelper.Domain.Entity.Abstract; -public class CurrencyResult: Abstract.Entity +namespace PoEGamblingHelper.Domain.Entity.Stats { - public string Name { get; set; } = string.Empty; - public decimal ChaosEquivalent { get; set; } + public class CurrencyResult : Entity + { + public string Name { get; set; } = string.Empty; + public decimal ChaosEquivalent { get; set; } + } } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/Result.cs b/src/Domain/Entity/Stats/Result.cs index d0ea84b6..8dc5f179 100644 --- a/src/Domain/Entity/Stats/Result.cs +++ b/src/Domain/Entity/Stats/Result.cs @@ -1,11 +1,13 @@ -using Domain.Entity.Gem; +using System; +using PoEGamblingHelper.Domain.Entity.Abstract; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Domain.Entity.Stats; - -public class Result: Abstract.Entity +namespace PoEGamblingHelper.Domain.Entity.Stats { - - public GemTradeData GemTradeData { get; set; } - public decimal CurrencyValue { get; set; } - public CurrencyResult CurrencyResult { get; set; } + public class Result : Entity + { + public GemTradeData GemTradeData { get; set; } + public decimal CurrencyValue { get; set; } + public CurrencyResult CurrencyResult { get; set; } + } } \ No newline at end of file diff --git a/src/Domain/Entity/TempleCost.cs b/src/Domain/Entity/TempleCost.cs index 228b18ab..0c59e57c 100644 --- a/src/Domain/Entity/TempleCost.cs +++ b/src/Domain/Entity/TempleCost.cs @@ -1,10 +1,13 @@ -using Domain.Entity.Abstract; +using System; +using System.Linq; +using PoEGamblingHelper.Domain.Entity.Abstract; -namespace Domain.Entity; - -public class TempleCost : Entity +namespace PoEGamblingHelper.Domain.Entity { - public DateTime TimeStamp { get; set; } = DateTime.Now.ToUniversalTime(); - public decimal[] ChaosValue { get; set; } = Array.Empty(); - public decimal AverageChaosValue() { return ChaosValue.Average(); } + public class TempleCost : Entity + { + public DateTime TimeStamp { get; set; } = DateTime.Now.ToUniversalTime(); + public decimal[] ChaosValue { get; set; } = Array.Empty(); + public decimal AverageChaosValue() { return ChaosValue.Average(); } + } } \ No newline at end of file diff --git a/src/Domain/QueryParameters/GemDataQuery.cs b/src/Domain/QueryParameters/GemDataQuery.cs deleted file mode 100644 index ca0a1e69..00000000 --- a/src/Domain/QueryParameters/GemDataQuery.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Domain.QueryParameters; - -public class GemDataQuery -{ - public string SearchText { get; set; } = string.Empty; - public Sort Sort { get; init; } = Sort.AverageProfitPerTryDesc; - public GemType GemType { get; init; } = GemType.All; - public bool OnlyShowProfitable { get; init; } - public bool ShowAlternateQuality { get; init; } - public bool ShowVaal { get; set; } = false; - public decimal? PricePerTryFrom { get; set; } - public decimal? PricePerTryTo { get; set; } -} \ No newline at end of file diff --git a/src/Domain/QueryParameters/GemType.cs b/src/Domain/QueryParameters/GemType.cs deleted file mode 100644 index 1d83bc46..00000000 --- a/src/Domain/QueryParameters/GemType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Domain.QueryParameters; - -public enum GemType -{ - All, - Exceptional, - Awakened, - RegularSupport, - Skill -} \ No newline at end of file diff --git a/src/Domain/QueryParameters/Page.cs b/src/Domain/QueryParameters/Page.cs deleted file mode 100644 index cd6a4f4a..00000000 --- a/src/Domain/QueryParameters/Page.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Domain.QueryParameters; - -public class Page -{ - public IEnumerable Content { get; init; } = null!; - public int CurrentPage { get; init; } - public bool LastPage { get; init; } -} \ No newline at end of file diff --git a/src/Domain/QueryParameters/PageRequest.cs b/src/Domain/QueryParameters/PageRequest.cs deleted file mode 100644 index f6a777b0..00000000 --- a/src/Domain/QueryParameters/PageRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Domain.QueryParameters; - -public class PageRequest -{ - private int _pageSize; - public int PageNumber { get; set; } - - public int PageSize - { - get => _pageSize; - set => _pageSize = value <= 0 ? 0 : value; - } -} \ No newline at end of file diff --git a/src/Domain/QueryParameters/Sort.cs b/src/Domain/QueryParameters/Sort.cs deleted file mode 100644 index ad4c7ad1..00000000 --- a/src/Domain/QueryParameters/Sort.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Domain.QueryParameters; - -public enum Sort -{ - CostPerTryAsc, - CostPerTryDesc, - AverageProfitPerTryAsc, - AverageProfitPerTryDesc, - MaxProfitPerTryAsc, - MaxProfitPerTryDesc -} \ No newline at end of file diff --git a/src/Domain/Util/ExtensionMethods.cs b/src/Domain/Util/ExtensionMethods.cs index b9a182eb..5a880290 100644 --- a/src/Domain/Util/ExtensionMethods.cs +++ b/src/Domain/Util/ExtensionMethods.cs @@ -1,8 +1,9 @@ -using Domain.Entity.Gem; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Domain.Util; - -public static class ExtensionMethods +namespace PoEGamblingHelper.Domain.Util { - public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } + public static class ExtensionMethods + { + public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } + } } \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/BackgroundJob.cs b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs new file mode 100644 index 00000000..18b62ac2 --- /dev/null +++ b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; + +namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; + +public abstract class BackgroundJob : BackgroundService +{ + private readonly TimeSpan _delay; + + protected BackgroundJob() { _delay = TimeSpan.Zero; } + protected BackgroundJob(TimeSpan delay) { _delay = delay; } + + protected BackgroundJob(TimeOnly startTime) + { + // now 23Uhr, start: 13uhr + // 13:00 - 23:00 = -10h WRONG -> + 24 + // 13:00 - 03:00 = 10h RIGHT + _delay = startTime.ToTimeSpan().Subtract(DateTime.UtcNow.TimeOfDay); + if (_delay < TimeSpan.Zero) _delay = _delay.Add(TimeSpan.FromHours(24)); + } + + protected abstract TimeSpan Interval(); + protected abstract Task ExecuteJobAsync(CancellationToken stoppingToken); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await Task.Delay(_delay, stoppingToken); + while (!stoppingToken.IsCancellationRequested) + { + await ExecuteJobAsync(stoppingToken); + await Task.Delay(Interval(), stoppingToken); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs new file mode 100644 index 00000000..7bf4a763 --- /dev/null +++ b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Services; + +namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; + +public class FetchLeagueJob : BackgroundJob +{ + private readonly IDataFetchService _dataFetchService; + + private readonly TimeSpan _interval = TimeSpan.FromHours(6); + private readonly ILogger _logger; + + public FetchLeagueJob(ILogger logger, IDataFetchService dataFetchService) + { + _logger = logger; + _dataFetchService = dataFetchService; + } + + protected override TimeSpan Interval() { return _interval; } + + protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) + { + try + { + await _dataFetchService.FetchCurrentLeague(); + } + catch (PoeGamblingHelperException e) + { + _logger.LogError("{Exception}", e); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs new file mode 100644 index 00000000..ec866116 --- /dev/null +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.OutputCaching; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; + +namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; + +public class FetchPriceDataJob : BackgroundJob +{ + private readonly IOutputCacheStore _cache; + private readonly string _cacheTag = ""; //TODO + private readonly IDataFetchService _dataFetchService; + + private readonly TimeSpan _interval = TimeSpan.FromHours(6); //TODO + private readonly ILeagueService _leagueService; + private readonly ILogger _logger; + + public FetchPriceDataJob(ILogger logger, + IDataFetchService dataFetchService, + ILeagueService leagueService, + IOutputCacheStore cache) + { + _logger = logger; + _dataFetchService = dataFetchService; + _leagueService = leagueService; + _cache = cache; + } + + protected override TimeSpan Interval() { return _interval; } + + protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) + { + League league; + try + { + league = _leagueService.GetCurrentLeague(); + } + catch (NoLeagueDataException e) + { + _logger.LogError("Could not Fetch Price Data, because League could not be get: {Exception}", e); + return; + } + + #region Fetch Data + + try + { + await _dataFetchService.FetchCurrencyData(league); + } + catch (PoeGamblingHelperException e) + { + _logger.LogError("Could not Fetch CurrencyData: {Exception}", e); + } + + try + { + await _dataFetchService.FetchTemplePriceData(league); + } + catch (PoeGamblingHelperException e) + { + _logger.LogError("Could not Fetch TemplePriceData: {Exception}", e); + } + + try + { + await _dataFetchService.FetchGemPriceData(league); + } + catch (PoeGamblingHelperException e) + { + _logger.LogError("Could not Fetch GemPriceData: {Exception}", e); + } + + #endregion + + await _cache.EvictByTagAsync(_cacheTag, stoppingToken); + _logger.LogDebug("Cache cleared"); + } +} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs new file mode 100644 index 00000000..26d4382c --- /dev/null +++ b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs @@ -0,0 +1,19 @@ +using PoEGamblingHelper.Application.Services; + +namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; + +public class LogAnalyticsJob : BackgroundJob +{ + private readonly IAnalyticsService _analyticsService; + + private readonly TimeSpan _interval = TimeSpan.FromDays(1); + + public LogAnalyticsJob(IAnalyticsService analyticsService) { _analyticsService = analyticsService; } + + protected override TimeSpan Interval() { return _interval; } + + protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) + { + await _analyticsService.LogYesterdaysViews(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Services/ApplicationDbContext.cs b/src/Infrastructure/Database/ApplicationDbContext.cs similarity index 64% rename from src/Infrastructure/Services/ApplicationDbContext.cs rename to src/Infrastructure/Database/ApplicationDbContext.cs index 05c4109d..e2ff7073 100644 --- a/src/Infrastructure/Services/ApplicationDbContext.cs +++ b/src/Infrastructure/Database/ApplicationDbContext.cs @@ -1,12 +1,11 @@ -using System.Text.Json; -using Application.Services; -using Domain.Entity; -using Domain.Entity.Analytics; -using Domain.Entity.Gem; -using Domain.Entity.Stats; +using System.Reflection; using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Analytics; +using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Domain.Entity.Stats; -namespace Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.Database; public class ApplicationDbContext : DbContext, IApplicationDbContext { @@ -24,11 +23,7 @@ public ApplicationDbContext(DbContextOptions options) : ba protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); - var options = new JsonSerializerOptions(); - modelBuilder.Entity().Property(p => p.ChaosValue) - .HasConversion( - v => JsonSerializer.Serialize(v, options), - v => JsonSerializer.Deserialize(v, options)!); } } \ No newline at end of file diff --git a/src/Infrastructure/Database/Configuration/EntityConfiguration.cs b/src/Infrastructure/Database/Configuration/EntityConfiguration.cs new file mode 100644 index 00000000..242c27d9 --- /dev/null +++ b/src/Infrastructure/Database/Configuration/EntityConfiguration.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PoEGamblingHelper.Domain.Entity.Abstract; + +namespace PoEGamblingHelper.Infrastructure.Database.Configuration; + +public class EntityGuidConfiguration : IEntityTypeConfiguration> +{ + public void Configure(EntityTypeBuilder> builder) + { + builder.HasKey(e => e.Id); + builder.Property(e => e.Id) + .ValueGeneratedOnAdd(); + } +} + +public class EntityLongConfiguration : IEntityTypeConfiguration> +{ + public void Configure(EntityTypeBuilder> builder) + { + builder.HasKey(e => e.Id); + builder.Property(e => e.Id) + .ValueGeneratedOnAdd(); + } +} + +public class EntityStringConfiguration : IEntityTypeConfiguration> +{ + public void Configure(EntityTypeBuilder> builder) + { + builder.HasKey(e => e.Id); + builder.Property(e => e.Id) + .ValueGeneratedOnAdd(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs b/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs new file mode 100644 index 00000000..77c05bf5 --- /dev/null +++ b/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PoEGamblingHelper.Domain.Entity; + +namespace PoEGamblingHelper.Infrastructure.Database.Configuration; + +public class TempleCostConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + var options = new JsonSerializerOptions(); + builder.Property(p => p.ChaosValue) + .HasConversion( + v => JsonSerializer.Serialize(v, options), + v => JsonSerializer.Deserialize(v, options)!); + } +} \ No newline at end of file diff --git a/src/Infrastructure/MigrateDatabaseExtension.cs b/src/Infrastructure/Database/MigrateDatabaseExtension.cs similarity index 62% rename from src/Infrastructure/MigrateDatabaseExtension.cs rename to src/Infrastructure/Database/MigrateDatabaseExtension.cs index 10945f27..c084d64a 100644 --- a/src/Infrastructure/MigrateDatabaseExtension.cs +++ b/src/Infrastructure/Database/MigrateDatabaseExtension.cs @@ -1,18 +1,16 @@ -using Application.Services; -using Infrastructure.Services; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Infrastructure; +namespace PoEGamblingHelper.Infrastructure.Database; public static class MigrateDatabaseExtensions { public static void MigrateDatabase(this WebApplication app) { using var scope = app.Services.CreateScope(); - var contextFactory = scope.ServiceProvider.GetRequiredService(); - var context = (ApplicationDbContext)contextFactory.CreateDbContext(); + var contextFactory = scope.ServiceProvider.GetRequiredService>(); + using var context = contextFactory.CreateDbContext(); if (context.Database.IsNpgsql()) context.Database.Migrate(); } } \ No newline at end of file diff --git a/src/Infrastructure/Exceptions/DbConcurrencyException.cs b/src/Infrastructure/Exceptions/DbConcurrencyException.cs new file mode 100644 index 00000000..e7b99794 --- /dev/null +++ b/src/Infrastructure/Exceptions/DbConcurrencyException.cs @@ -0,0 +1,8 @@ +using PoEGamblingHelper.Application.Exception.Abstract; + +namespace PoEGamblingHelper.Infrastructure.Exceptions; + +public class DbConcurrencyException : PoeGamblingHelperException +{ + public DbConcurrencyException(string? message) : base(message) { } +} \ No newline at end of file diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index eb9253d5..a8b159fd 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -16,6 +16,7 @@ + @@ -25,4 +26,7 @@ + + + diff --git a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs b/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs index d4e5e6a3..d0b89c30 100644 --- a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs +++ b/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; #nullable disable diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs index 536bfe07..7cb33420 100644 --- a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs +++ b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; #nullable disable diff --git a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs b/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs index 43b44ea7..c58f70c0 100644 --- a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs +++ b/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; #nullable disable diff --git a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs b/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs index c33dc2c7..39f92733 100644 --- a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs +++ b/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; #nullable disable diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index f7598cd8..bcf7d220 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; #nullable disable diff --git a/src/Infrastructure/Repositories/CurrencyRepository.cs b/src/Infrastructure/Repositories/CurrencyRepository.cs new file mode 100644 index 00000000..994a4148 --- /dev/null +++ b/src/Infrastructure/Repositories/CurrencyRepository.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.Repositories; + +public class CurrencyRepository +{ + private readonly IDbContextFactory _dbContextFactory; + + public CurrencyRepository(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public async IAsyncEnumerable GetAll() + { + await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync(); + await foreach (var item in applicationDbContext.Currency.AsAsyncEnumerable()) + yield return item; + } +} \ No newline at end of file diff --git a/src/Infrastructure/ConfigureServices.cs b/src/Infrastructure/ServiceCollectionExtensions.cs similarity index 62% rename from src/Infrastructure/ConfigureServices.cs rename to src/Infrastructure/ServiceCollectionExtensions.cs index 3e596213..365dba76 100644 --- a/src/Infrastructure/ConfigureServices.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -1,15 +1,28 @@ -using Application.Services; -using Infrastructure.Services; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Infrastructure.BackgroundJobs; +using PoEGamblingHelper.Infrastructure.Database; +using PoEGamblingHelper.Infrastructure.Services; -namespace Infrastructure; +namespace PoEGamblingHelper.Infrastructure; -public static class ConfigureServices +public static class ServiceCollectionExtensions { - public static void AddInfrastructureServices(this IServiceCollection services, - IConfiguration configuration) + public static void AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddDatabase(configuration); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddBackgroundJobs(); + } + + private static void AddDatabase(this IServiceCollection services, IConfiguration configuration) { if (configuration.GetValue("UseInMemoryDatabase")) { @@ -27,16 +40,12 @@ public static void AddInfrastructureServices(this IServiceCollection services, builder => builder.MigrationsAssembly(assemblyName) )); } + } - services.AddSingleton( - provider => new ApplicationDbContextFactory( - provider.GetRequiredService>() - ) - ); - - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + private static void AddBackgroundJobs(this IServiceCollection services) + { + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); } } \ No newline at end of file diff --git a/src/Infrastructure/Services/ApplicationDbContextFactory.cs b/src/Infrastructure/Services/ApplicationDbContextFactory.cs deleted file mode 100644 index 5fe3ce76..00000000 --- a/src/Infrastructure/Services/ApplicationDbContextFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Application.Services; -using Microsoft.EntityFrameworkCore; - -namespace Infrastructure.Services; - -public class ApplicationDbContextFactory : IApplicationDbContextFactory -{ - private readonly IDbContextFactory _dbContextFactory; - - public ApplicationDbContextFactory(IDbContextFactory dbContextFactory) - { - _dbContextFactory = dbContextFactory; - } - - public IApplicationDbContext CreateDbContext() { return _dbContextFactory.CreateDbContext(); } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/DataFetchService.cs b/src/Infrastructure/Services/DataFetchService.cs index 15a7ad4a..bfcc09ee 100644 --- a/src/Infrastructure/Services/DataFetchService.cs +++ b/src/Infrastructure/Services/DataFetchService.cs @@ -2,16 +2,15 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.RegularExpressions; -using Application.Services; -using Domain.Entity; -using Domain.Exception; using HtmlAgilityPack; -using Infrastructure.Services.FetchDtos; -using Infrastructure.Util; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Infrastructure.Util; -namespace Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.Services; public partial class DataFetchService : IDataFetchService, IDisposable { diff --git a/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs b/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs index 62a69f6b..96ea5146 100644 --- a/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs +++ b/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class CurrencyPriceData { diff --git a/src/Infrastructure/Services/FetchDtos/GemPriceData.cs b/src/Infrastructure/Services/FetchDtos/GemPriceData.cs index 4dfacb88..386009b3 100644 --- a/src/Infrastructure/Services/FetchDtos/GemPriceData.cs +++ b/src/Infrastructure/Services/FetchDtos/GemPriceData.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class GemPriceData { diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs index e45bd6cd..774ab7cf 100644 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs +++ b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class PoeNinjaCurrencyData { diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs index 392e8948..c197e52f 100644 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs +++ b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class PoeNinjaCurrencyDetails { diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs index 6349db82..df0b73ea 100644 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs +++ b/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs @@ -1,6 +1,6 @@ -using Domain.Entity.Gem; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class PoeNinjaGemData { diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntry.cs b/src/Infrastructure/Services/FetchDtos/TradeEntry.cs index ea939a84..c3b08b0e 100644 --- a/src/Infrastructure/Services/FetchDtos/TradeEntry.cs +++ b/src/Infrastructure/Services/FetchDtos/TradeEntry.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class TradeEntry { diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs index 5255b1b8..75dec861 100644 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs +++ b/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class TradeEntryListing { diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs index 7d0fd652..7171eba6 100644 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs +++ b/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs @@ -1,7 +1,7 @@ -using Domain.Entity; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Domain.Entity; -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class TradeEntryListingPrice { diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs index d3086979..5f8b82a6 100644 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs +++ b/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class TradeEntryResult { diff --git a/src/Infrastructure/Services/FetchDtos/TradeResults.cs b/src/Infrastructure/Services/FetchDtos/TradeResults.cs index a8ea7003..412988a8 100644 --- a/src/Infrastructure/Services/FetchDtos/TradeResults.cs +++ b/src/Infrastructure/Services/FetchDtos/TradeResults.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Services.FetchDtos; +namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; public class TradeResults { diff --git a/src/Infrastructure/Util/ExtensionMethods.cs b/src/Infrastructure/Util/ExtensionMethods.cs index a5e332dc..175b1426 100644 --- a/src/Infrastructure/Util/ExtensionMethods.cs +++ b/src/Infrastructure/Util/ExtensionMethods.cs @@ -1,16 +1,11 @@ -using Domain.Entity.Gem; -using Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Application.Extensions; +using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; -namespace Infrastructure.Util; +namespace PoEGamblingHelper.Infrastructure.Util; public static class ExtensionMethods { - public static bool EqualsIgnoreCase(this string? a, string? b) - { - return a?.Equals(b, StringComparison.InvariantCultureIgnoreCase) - ?? b is null; - } - public static GemData ToGemData(this IGrouping group, IEnumerable gemTradeData) { diff --git a/src/Infrastructure/Util/PoeToolUrls.cs b/src/Infrastructure/Util/PoeToolUrls.cs index 669c6d2c..889d52b9 100644 --- a/src/Infrastructure/Util/PoeToolUrls.cs +++ b/src/Infrastructure/Util/PoeToolUrls.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Util; +namespace PoEGamblingHelper.Infrastructure.Util; public static class PoeToolUrls { diff --git a/src/Shared/QueryParameters/GemDataQuery.cs b/src/Shared/QueryParameters/GemDataQuery.cs new file mode 100644 index 00000000..f9c2b610 --- /dev/null +++ b/src/Shared/QueryParameters/GemDataQuery.cs @@ -0,0 +1,14 @@ +namespace PoEGamblingHelper.Shared.QueryParameters +{ + public class GemDataQuery + { + public string SearchText { get; set; } = string.Empty; + public Sort Sort { get; init; } = Sort.AverageProfitPerTryDesc; + public GemType GemType { get; init; } = GemType.All; + public bool OnlyShowProfitable { get; init; } + public bool ShowAlternateQuality { get; init; } + public bool ShowVaal { get; set; } = false; + public decimal? PricePerTryFrom { get; set; } + public decimal? PricePerTryTo { get; set; } + } +} \ No newline at end of file diff --git a/src/Shared/QueryParameters/GemType.cs b/src/Shared/QueryParameters/GemType.cs new file mode 100644 index 00000000..9b5af493 --- /dev/null +++ b/src/Shared/QueryParameters/GemType.cs @@ -0,0 +1,11 @@ +namespace PoEGamblingHelper.Shared.QueryParameters +{ + public enum GemType + { + All, + Exceptional, + Awakened, + RegularSupport, + Skill + } +} \ No newline at end of file diff --git a/src/Shared/QueryParameters/Page.cs b/src/Shared/QueryParameters/Page.cs new file mode 100644 index 00000000..2da7520d --- /dev/null +++ b/src/Shared/QueryParameters/Page.cs @@ -0,0 +1,9 @@ +namespace PoEGamblingHelper.Shared.QueryParameters +{ + public class Page + { + public IEnumerable Content { get; init; } = null!; + public int CurrentPage { get; init; } + public bool LastPage { get; init; } + } +} \ No newline at end of file diff --git a/src/Shared/QueryParameters/PageRequest.cs b/src/Shared/QueryParameters/PageRequest.cs new file mode 100644 index 00000000..1fc049fa --- /dev/null +++ b/src/Shared/QueryParameters/PageRequest.cs @@ -0,0 +1,14 @@ +namespace PoEGamblingHelper.Shared.QueryParameters +{ + public class PageRequest + { + private int _pageSize; + public int PageNumber { get; set; } + + public int PageSize + { + get => _pageSize; + set => _pageSize = value <= 0 ? 0 : value; + } + } +} \ No newline at end of file diff --git a/src/Shared/QueryParameters/Sort.cs b/src/Shared/QueryParameters/Sort.cs new file mode 100644 index 00000000..e364b370 --- /dev/null +++ b/src/Shared/QueryParameters/Sort.cs @@ -0,0 +1,12 @@ +namespace PoEGamblingHelper.Shared.QueryParameters +{ + public enum Sort + { + CostPerTryAsc, + CostPerTryDesc, + AverageProfitPerTryAsc, + AverageProfitPerTryDesc, + MaxProfitPerTryAsc, + MaxProfitPerTryDesc + } +} \ No newline at end of file diff --git a/src/Shared/Shared.csproj b/src/Shared/Shared.csproj new file mode 100644 index 00000000..e183d57d --- /dev/null +++ b/src/Shared/Shared.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.1 + + + diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index faba43c5..2b2f38e5 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -1,9 +1,8 @@ using Blazored.LocalStorage; -using Domain.Entity; -using Domain.Entity.Gem; -using Domain.QueryParameters; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Gem; using Web.Services.Interfaces; using Web.Shared.Model; diff --git a/src/Web/Services/Implementations/CurrencyService.cs b/src/Web/Services/Implementations/CurrencyService.cs index ad539044..f13d1dbb 100644 --- a/src/Web/Services/Implementations/CurrencyService.cs +++ b/src/Web/Services/Implementations/CurrencyService.cs @@ -1,5 +1,5 @@ using Blazored.Toast.Services; -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; using Web.Services.Interfaces; namespace Web.Services.Implementations; diff --git a/src/Web/Services/Implementations/GemService.cs b/src/Web/Services/Implementations/GemService.cs index 836c5a97..6a623925 100644 --- a/src/Web/Services/Implementations/GemService.cs +++ b/src/Web/Services/Implementations/GemService.cs @@ -1,8 +1,6 @@ using Blazored.Toast.Services; -using Domain.Entity.Gem; -using Domain.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; using Web.Services.Interfaces; -using Web.Util; namespace Web.Services.Implementations; diff --git a/src/Web/Services/Implementations/LeagueService.cs b/src/Web/Services/Implementations/LeagueService.cs index 3b1e1c3c..efe7f68e 100644 --- a/src/Web/Services/Implementations/LeagueService.cs +++ b/src/Web/Services/Implementations/LeagueService.cs @@ -1,5 +1,5 @@ using Blazored.Toast.Services; -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; using Web.Services.Interfaces; namespace Web.Services.Implementations; diff --git a/src/Web/Services/Implementations/TempleCostService.cs b/src/Web/Services/Implementations/TempleCostService.cs index c44801de..0348ea8e 100644 --- a/src/Web/Services/Implementations/TempleCostService.cs +++ b/src/Web/Services/Implementations/TempleCostService.cs @@ -1,5 +1,5 @@ using Blazored.Toast.Services; -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; using Web.Services.Interfaces; namespace Web.Services.Implementations; diff --git a/src/Web/Services/Interfaces/ICurrencyService.cs b/src/Web/Services/Interfaces/ICurrencyService.cs index 8bb35333..b9d0734a 100644 --- a/src/Web/Services/Interfaces/ICurrencyService.cs +++ b/src/Web/Services/Interfaces/ICurrencyService.cs @@ -1,4 +1,4 @@ -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; namespace Web.Services.Interfaces; diff --git a/src/Web/Services/Interfaces/IGemService.cs b/src/Web/Services/Interfaces/IGemService.cs index 65371ff5..7afc461e 100644 --- a/src/Web/Services/Interfaces/IGemService.cs +++ b/src/Web/Services/Interfaces/IGemService.cs @@ -1,5 +1,4 @@ -using Domain.Entity.Gem; -using Domain.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Web.Services.Interfaces; diff --git a/src/Web/Services/Interfaces/ILeagueService.cs b/src/Web/Services/Interfaces/ILeagueService.cs index fb84b722..198cda59 100644 --- a/src/Web/Services/Interfaces/ILeagueService.cs +++ b/src/Web/Services/Interfaces/ILeagueService.cs @@ -1,4 +1,4 @@ -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; namespace Web.Services.Interfaces; diff --git a/src/Web/Services/Interfaces/ITempleCostService.cs b/src/Web/Services/Interfaces/ITempleCostService.cs index deabbe83..5aa86c77 100644 --- a/src/Web/Services/Interfaces/ITempleCostService.cs +++ b/src/Web/Services/Interfaces/ITempleCostService.cs @@ -1,4 +1,4 @@ -using Domain.Entity; +using PoEGamblingHelper.Domain.Entity; namespace Web.Services.Interfaces; diff --git a/src/Web/Shared/Components/Filter.razor.cs b/src/Web/Shared/Components/Filter.razor.cs index d651a719..f787c628 100644 --- a/src/Web/Shared/Components/Filter.razor.cs +++ b/src/Web/Shared/Components/Filter.razor.cs @@ -1,8 +1,7 @@ using System.Text.RegularExpressions; using Blazored.LocalStorage; -using Domain.Entity; -using Domain.QueryParameters; using Microsoft.AspNetCore.Components; +using PoEGamblingHelper.Domain.Entity; using Web.Shared.Model; using Web.Util; diff --git a/src/Web/Shared/Components/GemStats.razor.cs b/src/Web/Shared/Components/GemStats.razor.cs index 6ade37ac..ec83d7fa 100644 --- a/src/Web/Shared/Components/GemStats.razor.cs +++ b/src/Web/Shared/Components/GemStats.razor.cs @@ -1,9 +1,8 @@ using System.Diagnostics; using Blazored.LocalStorage; -using Domain.Entity; -using Domain.Entity.Gem; -using Domain.Util; using Microsoft.AspNetCore.Components; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Gem; using Web.Shared.Model; using Web.Util; diff --git a/src/Web/Shared/Model/FilterValues.cs b/src/Web/Shared/Model/FilterValues.cs index a109eba5..bacd1029 100644 --- a/src/Web/Shared/Model/FilterValues.cs +++ b/src/Web/Shared/Model/FilterValues.cs @@ -1,5 +1,4 @@ -using Domain.Entity; -using Domain.QueryParameters; +using PoEGamblingHelper.Domain.Entity; namespace Web.Shared.Model; diff --git a/src/Web/Util/ExtensionMethods.cs b/src/Web/Util/ExtensionMethods.cs index 8adb2c33..5d2df4e7 100644 --- a/src/Web/Util/ExtensionMethods.cs +++ b/src/Web/Util/ExtensionMethods.cs @@ -2,10 +2,8 @@ using System.Net.Http.Json; using System.Text.RegularExpressions; using System.Web; -using Domain.Entity; -using Domain.Entity.Gem; -using Domain.Exception.Body; -using Domain.QueryParameters; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Web.Util; @@ -82,7 +80,9 @@ public static string ToQueryString(this PageRequest pageRequest) return $"?pageNumber={pageRequest.PageNumber}&pageSize={pageRequest.PageSize}"; } - public static string TradeUrl(this GemTradeData gemTradeData, League currentLeague, bool accurateLevel = true, + public static string TradeUrl(this GemTradeData gemTradeData, + League currentLeague, + bool accurateLevel = true, bool accurateQuality = false) { const string poeTradeUrl = "https://www.pathofexile.com/trade/search"; @@ -90,7 +90,8 @@ public static string TradeUrl(this GemTradeData gemTradeData, League currentLeag return $"{poeTradeUrl}/{currentLeague.Name}{queryKey}{gemTradeData.TradeQuery(accurateLevel, accurateQuality)}"; } - public static string TradeQuery(this GemTradeData gemTradeData, bool accurateLevel = true, + public static string TradeQuery(this GemTradeData gemTradeData, + bool accurateLevel = true, bool accurateQuality = false) { var gemAlternateQuality = -1; diff --git a/test/Application.Test/Services/GemServiceTest.cs b/test/Application.Test/Services/GemServiceTest.cs index 451cac2f..ae13c297 100644 --- a/test/Application.Test/Services/GemServiceTest.cs +++ b/test/Application.Test/Services/GemServiceTest.cs @@ -1,9 +1,7 @@ -using Application.Services; -using Domain.Entity.Gem; -using Domain.QueryParameters; -using FluentAssertions; -using MockQueryable.Moq; +using MockQueryable.Moq; using Moq; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Application.Test.Services; diff --git a/test/Application.Test/Services/InitServiceTest.cs b/test/Application.Test/Services/InitServiceTest.cs index 0be1e5f5..462387db 100644 --- a/test/Application.Test/Services/InitServiceTest.cs +++ b/test/Application.Test/Services/InitServiceTest.cs @@ -1,10 +1,9 @@ using System.Diagnostics; -using Application.Services; -using Domain.Entity; -using Domain.Exception; -using Microsoft.AspNetCore.OutputCaching; using Microsoft.Extensions.Logging; using Moq; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; #pragma warning disable CS8625 diff --git a/test/Application.Test/Services/LeagueServiceTest.cs b/test/Application.Test/Services/LeagueServiceTest.cs index 0e9f222e..8eac6cbf 100644 --- a/test/Application.Test/Services/LeagueServiceTest.cs +++ b/test/Application.Test/Services/LeagueServiceTest.cs @@ -1,8 +1,8 @@ -using Application.Services; -using Domain.Entity; -using Domain.Exception; -using FluentAssertions; +using FluentAssertions; using MockQueryable.Moq; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Domain.Entity; namespace Application.Test.Services; diff --git a/test/Domain.Test/Entity/GemDataTest.cs b/test/Domain.Test/Entity/GemDataTest.cs index 274e8d11..c58faa81 100644 --- a/test/Domain.Test/Entity/GemDataTest.cs +++ b/test/Domain.Test/Entity/GemDataTest.cs @@ -1,5 +1,4 @@ -using Domain.Entity.Gem; -using FluentAssertions; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Domain.Test.Entity; @@ -187,7 +186,10 @@ public void ValueTest(string name, int expectedMiddle, int expectedMiddleAwakene [InlineData("Enlighten Support", 25, 15, 45, 35)] [InlineData("Brutality Support", 105, 95, 65, 55)] [InlineData("Seismic Trap", 105, 95, 65, 55)] - public void ProfitTest(string name, int rawValue, int expectedMiddle, int rawValueAwakened, + public void ProfitTest(string name, + int rawValue, + int expectedMiddle, + int rawValueAwakened, int expectedMiddleAwakened) { var gemData = new GemData diff --git a/test/Domain.Test/Entity/TempleCostTest.cs b/test/Domain.Test/Entity/TempleCostTest.cs index ef8b6b0f..2a00dcd7 100644 --- a/test/Domain.Test/Entity/TempleCostTest.cs +++ b/test/Domain.Test/Entity/TempleCostTest.cs @@ -1,5 +1,5 @@ -using Domain.Entity; using FluentAssertions; +using PoEGamblingHelper.Domain.Entity; namespace Domain.Test.Entity; diff --git a/test/Domain.Test/QueryParameters/PageRequestTest.cs b/test/Domain.Test/QueryParameters/PageRequestTest.cs index 5693b344..dfe8f70b 100644 --- a/test/Domain.Test/QueryParameters/PageRequestTest.cs +++ b/test/Domain.Test/QueryParameters/PageRequestTest.cs @@ -1,6 +1,3 @@ -using Domain.QueryParameters; -using FluentAssertions; - namespace Domain.Test.QueryParameters; public class PageRequestTest diff --git a/test/Domain.Test/Util/ExtensionMethodsTest.cs b/test/Domain.Test/Util/ExtensionMethodsTest.cs index b7e7e862..9388fc78 100644 --- a/test/Domain.Test/Util/ExtensionMethodsTest.cs +++ b/test/Domain.Test/Util/ExtensionMethodsTest.cs @@ -1,6 +1,4 @@ -using Domain.Entity.Gem; -using Domain.Util; -using FluentAssertions; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Domain.Test.Util; diff --git a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs index 65cf92e7..efba904c 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; namespace Infrastructure.Test.Services.FetchDtos; diff --git a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs index ab826d24..70714e53 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; namespace Infrastructure.Test.Services.FetchDtos; diff --git a/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs b/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs index 9e83ec27..30bb4177 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs @@ -1,7 +1,7 @@ -using Domain.Entity; -using FluentAssertions; -using Infrastructure.Services.FetchDtos; +using FluentAssertions; using MockQueryable.Moq; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; namespace Infrastructure.Test.Services.FetchDtos; diff --git a/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs b/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs index 72d3b0b6..805d6b37 100644 --- a/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs +++ b/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs @@ -1,7 +1,7 @@ -using Domain.Entity.Gem; -using FluentAssertions; -using Infrastructure.Services.FetchDtos; -using Infrastructure.Util; +using FluentAssertions; +using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Infrastructure.Util; namespace Infrastructure.Test.Util; diff --git a/test/Web.Test/Shared/Model/FilterValuesTest.cs b/test/Web.Test/Shared/Model/FilterValuesTest.cs index 20d2569a..f736a74e 100644 --- a/test/Web.Test/Shared/Model/FilterValuesTest.cs +++ b/test/Web.Test/Shared/Model/FilterValuesTest.cs @@ -1,6 +1,4 @@ -using Domain.QueryParameters; -using FluentAssertions; -using Web.Shared.Model; +using Web.Shared.Model; namespace Web.Test.Shared.Model; diff --git a/test/Web.Test/Util/ExtensionFunctionsTest.cs b/test/Web.Test/Util/ExtensionFunctionsTest.cs index 8ae93043..2458f008 100644 --- a/test/Web.Test/Util/ExtensionFunctionsTest.cs +++ b/test/Web.Test/Util/ExtensionFunctionsTest.cs @@ -1,5 +1,4 @@ using System.Globalization; -using Domain.QueryParameters; using FluentAssertions; using Web.Util; using Xunit.Abstractions; From 02283b243f7ebdb1ce3625d06cd8f6ff023d6a7e Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 14:13:15 +0200 Subject: [PATCH 022/129] fix Imports --- src/Api/ConfigureCache.cs | 2 +- src/Api/ConfigureRateLimiter.cs | 5 +++-- src/Api/Controllers/GemController.cs | 3 +-- src/Api/Controllers/LeagueController.cs | 3 +-- src/Api/Controllers/StatsController.cs | 3 +-- src/Api/Controllers/TempleController.cs | 3 +-- src/Api/Filters/HttpResponseExceptionFilter.cs | 2 +- src/Api/Program.cs | 3 +-- src/Application/Util/ExtensionMethods.cs | 6 +++++- src/Domain/Util/ExtensionMethods.cs | 9 --------- src/Infrastructure/Database/ApplicationDbContext.cs | 2 +- src/Infrastructure/Services/DataFetchService.cs | 7 +++++-- src/Web/Pages/GamblingHelper.razor.cs | 7 ++++--- src/Web/Pages/GamblingHelper.razor.css | 2 -- src/Web/Pages/Stats.razor.cs | 4 ++-- src/Web/Program.cs | 6 +++--- src/Web/Services/Implementations/CurrencyService.cs | 4 ++-- src/Web/Services/Implementations/GemService.cs | 5 +++-- src/Web/Services/Implementations/LeagueService.cs | 4 ++-- src/Web/Services/Implementations/ScollInfoService.cs | 4 ++-- src/Web/Services/Implementations/TempleCostService.cs | 4 ++-- src/Web/Services/Implementations/UpdateService.cs | 4 ++-- src/Web/Services/Interfaces/HttpService.cs | 5 ++--- src/Web/Services/Interfaces/ICurrencyService.cs | 2 +- src/Web/Services/Interfaces/IGemService.cs | 3 ++- src/Web/Services/Interfaces/ILeagueService.cs | 2 +- src/Web/Services/Interfaces/IScrollInfoService.cs | 2 +- src/Web/Services/Interfaces/ITempleCostService.cs | 2 +- src/Web/Services/Interfaces/IUpdateService.cs | 4 ++-- src/Web/Shared/Components/Filter.razor.cs | 7 ++++--- src/Web/Shared/Components/Footer.razor.cs | 4 ++-- src/Web/Shared/Components/GemStats.razor.cs | 7 ++++--- src/Web/Shared/Components/Theme.razor.cs | 2 +- src/Web/Shared/Model/FilterValues.cs | 3 ++- src/Web/Util/ExtensionMethods.cs | 4 +++- src/Web/Web.csproj | 2 ++ test/Web.Test/Shared/Model/FilterValuesTest.cs | 2 +- test/Web.Test/Util/ExtensionFunctionsTest.cs | 2 +- 38 files changed, 73 insertions(+), 72 deletions(-) delete mode 100644 src/Domain/Util/ExtensionMethods.cs diff --git a/src/Api/ConfigureCache.cs b/src/Api/ConfigureCache.cs index 3e0cd641..1211ba0b 100644 --- a/src/Api/ConfigureCache.cs +++ b/src/Api/ConfigureCache.cs @@ -1,4 +1,4 @@ -namespace Api; +namespace PoEGamblingHelper.Api; public static class ConfigureCache { diff --git a/src/Api/ConfigureRateLimiter.cs b/src/Api/ConfigureRateLimiter.cs index c0cdd30b..b19c4b14 100644 --- a/src/Api/ConfigureRateLimiter.cs +++ b/src/Api/ConfigureRateLimiter.cs @@ -2,7 +2,7 @@ using System.Net; using System.Threading.RateLimiting; -namespace Api; +namespace PoEGamblingHelper.Api; public static class ConfigureRateLimiter { @@ -62,7 +62,8 @@ private static RateLimitPartition GetGlobalRateLimiter(IConfiguration co } private static RateLimitPartition GetIpAddressRateLimiter( - IConfiguration configuration, HttpContext context) + IConfiguration configuration, + HttpContext context) { var remoteIpAddress = context.Connection.RemoteIpAddress; if (IPAddress.IsLoopback(remoteIpAddress!)) return RateLimitPartition.GetNoLimiter(IPAddress.Loopback); diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index 5519f13a..3e64b4b1 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -1,10 +1,9 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Controllers; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity.Gem; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public class GemController : ApiControllerBase { diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index c79dd37a..188a3616 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -1,10 +1,9 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Controllers; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public class LeagueController : ApiControllerBase { diff --git a/src/Api/Controllers/StatsController.cs b/src/Api/Controllers/StatsController.cs index 3328811c..43cd1087 100644 --- a/src/Api/Controllers/StatsController.cs +++ b/src/Api/Controllers/StatsController.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Mvc; -using PoEGamblingHelper.Api.Controllers; using PoEGamblingHelper.Domain.Entity.Stats; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public class StatsController : ApiControllerBase { diff --git a/src/Api/Controllers/TempleController.cs b/src/Api/Controllers/TempleController.cs index 2e0b0da1..091a98f0 100644 --- a/src/Api/Controllers/TempleController.cs +++ b/src/Api/Controllers/TempleController.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Controllers; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; -namespace Api.Controllers; +namespace PoEGamblingHelper.Api.Controllers; public class TempleController : ApiControllerBase { diff --git a/src/Api/Filters/HttpResponseExceptionFilter.cs b/src/Api/Filters/HttpResponseExceptionFilter.cs index 522fa9ff..80f00b39 100644 --- a/src/Api/Filters/HttpResponseExceptionFilter.cs +++ b/src/Api/Filters/HttpResponseExceptionFilter.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using PoEGamblingHelper.Application.Exception.Abstract; -namespace Api.Filters; +namespace PoEGamblingHelper.Api.Filters; // ReSharper disable once ClassNeverInstantiated.Global internal class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter diff --git a/src/Api/Program.cs b/src/Api/Program.cs index 9928bb41..e1468959 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,8 +1,7 @@ using System.Globalization; -using Api; -using Api.Filters; using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Api; +using PoEGamblingHelper.Api.Filters; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure; using PoEGamblingHelper.Infrastructure.Database; diff --git a/src/Application/Util/ExtensionMethods.cs b/src/Application/Util/ExtensionMethods.cs index 179f30a9..7e214b09 100644 --- a/src/Application/Util/ExtensionMethods.cs +++ b/src/Application/Util/ExtensionMethods.cs @@ -1,4 +1,6 @@ -namespace PoEGamblingHelper.Application.Util; +using PoEGamblingHelper.Domain.Entity.Gem; + +namespace PoEGamblingHelper.Application.Util; public static class ExtensionMethods { @@ -6,4 +8,6 @@ public static (int skipSize, int takeSize) ConvertToSizes(this PageRequest pageR { return (pageRequest.PageSize * pageRequest.PageNumber, pageRequest.PageSize); } + + public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } } \ No newline at end of file diff --git a/src/Domain/Util/ExtensionMethods.cs b/src/Domain/Util/ExtensionMethods.cs deleted file mode 100644 index 5a880290..00000000 --- a/src/Domain/Util/ExtensionMethods.cs +++ /dev/null @@ -1,9 +0,0 @@ -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Domain.Util -{ - public static class ExtensionMethods - { - public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } - } -} \ No newline at end of file diff --git a/src/Infrastructure/Database/ApplicationDbContext.cs b/src/Infrastructure/Database/ApplicationDbContext.cs index e2ff7073..548c9cf2 100644 --- a/src/Infrastructure/Database/ApplicationDbContext.cs +++ b/src/Infrastructure/Database/ApplicationDbContext.cs @@ -7,7 +7,7 @@ namespace PoEGamblingHelper.Infrastructure.Database; -public class ApplicationDbContext : DbContext, IApplicationDbContext +public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } public virtual DbSet Currency => Set(); diff --git a/src/Infrastructure/Services/DataFetchService.cs b/src/Infrastructure/Services/DataFetchService.cs index bfcc09ee..c8e53077 100644 --- a/src/Infrastructure/Services/DataFetchService.cs +++ b/src/Infrastructure/Services/DataFetchService.cs @@ -3,10 +3,12 @@ using System.Net.Http.Json; using System.Text.RegularExpressions; using HtmlAgilityPack; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; using PoEGamblingHelper.Infrastructure.Services.FetchDtos; using PoEGamblingHelper.Infrastructure.Util; @@ -14,14 +16,15 @@ namespace PoEGamblingHelper.Infrastructure.Services; public partial class DataFetchService : IDataFetchService, IDisposable { - private readonly IApplicationDbContextFactory _applicationDbContextFactory; + private readonly IDbContextFactory _applicationDbContextFactory; private readonly HtmlWeb _htmlLoader = new(); private readonly HttpClient _httpClient = new(); private readonly MediaTypeHeaderValue _jsonMediaTypeHeader = MediaTypeHeaderValue.Parse("application/json"); private readonly ILogger _logger; private readonly string _templeQuery; - public DataFetchService(ILogger logger, IApplicationDbContextFactory applicationDbContextFactory) + public DataFetchService(ILogger logger, + IDbContextFactory applicationDbContextFactory) { _logger = logger; _applicationDbContextFactory = applicationDbContextFactory; diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 2b2f38e5..6e623ae3 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -3,10 +3,11 @@ using Microsoft.JSInterop; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; -using Web.Services.Interfaces; -using Web.Shared.Model; +using PoEGamblingHelper.Shared.QueryParameters; +using PoEGamblingHelper.Web.Services.Interfaces; +using PoEGamblingHelper.Web.Shared.Model; -namespace Web.Pages; +namespace PoEGamblingHelper.Web.Pages; public partial class GamblingHelper : IDisposable { diff --git a/src/Web/Pages/GamblingHelper.razor.css b/src/Web/Pages/GamblingHelper.razor.css index 45804ecd..87a01650 100644 --- a/src/Web/Pages/GamblingHelper.razor.css +++ b/src/Web/Pages/GamblingHelper.razor.css @@ -1,5 +1,3 @@ - - h1 { font-weight: 500; font-size: 64px; diff --git a/src/Web/Pages/Stats.razor.cs b/src/Web/Pages/Stats.razor.cs index dffecacd..ef90b903 100644 --- a/src/Web/Pages/Stats.razor.cs +++ b/src/Web/Pages/Stats.razor.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Components; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Pages; +namespace PoEGamblingHelper.Web.Pages; public partial class Stats : IDisposable { diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 67565736..0d3e20c8 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -3,9 +3,9 @@ using Blazored.Toast; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Web; -using Web.Services.Implementations; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web; +using PoEGamblingHelper.Web.Services.Implementations; +using PoEGamblingHelper.Web.Services.Interfaces; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); diff --git a/src/Web/Services/Implementations/CurrencyService.cs b/src/Web/Services/Implementations/CurrencyService.cs index f13d1dbb..4f241a41 100644 --- a/src/Web/Services/Implementations/CurrencyService.cs +++ b/src/Web/Services/Implementations/CurrencyService.cs @@ -1,8 +1,8 @@ using Blazored.Toast.Services; using PoEGamblingHelper.Domain.Entity; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class CurrencyService : HttpService, ICurrencyService { diff --git a/src/Web/Services/Implementations/GemService.cs b/src/Web/Services/Implementations/GemService.cs index 6a623925..d9c33cfc 100644 --- a/src/Web/Services/Implementations/GemService.cs +++ b/src/Web/Services/Implementations/GemService.cs @@ -1,8 +1,9 @@ using Blazored.Toast.Services; using PoEGamblingHelper.Domain.Entity.Gem; -using Web.Services.Interfaces; +using PoEGamblingHelper.Shared.QueryParameters; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class GemService : HttpService, IGemService { diff --git a/src/Web/Services/Implementations/LeagueService.cs b/src/Web/Services/Implementations/LeagueService.cs index efe7f68e..c1c0afbf 100644 --- a/src/Web/Services/Implementations/LeagueService.cs +++ b/src/Web/Services/Implementations/LeagueService.cs @@ -1,8 +1,8 @@ using Blazored.Toast.Services; using PoEGamblingHelper.Domain.Entity; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class LeagueService : HttpService, ILeagueService { diff --git a/src/Web/Services/Implementations/ScollInfoService.cs b/src/Web/Services/Implementations/ScollInfoService.cs index 750f7e2d..1b58a20d 100644 --- a/src/Web/Services/Implementations/ScollInfoService.cs +++ b/src/Web/Services/Implementations/ScollInfoService.cs @@ -1,7 +1,7 @@ using Microsoft.JSInterop; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class ScrollInfoService : IScrollInfoService, IDisposable { diff --git a/src/Web/Services/Implementations/TempleCostService.cs b/src/Web/Services/Implementations/TempleCostService.cs index 0348ea8e..85a5e684 100644 --- a/src/Web/Services/Implementations/TempleCostService.cs +++ b/src/Web/Services/Implementations/TempleCostService.cs @@ -1,8 +1,8 @@ using Blazored.Toast.Services; using PoEGamblingHelper.Domain.Entity; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class TempleCostService : HttpService, ITempleCostService { diff --git a/src/Web/Services/Implementations/UpdateService.cs b/src/Web/Services/Implementations/UpdateService.cs index fad3661f..21868ec3 100644 --- a/src/Web/Services/Implementations/UpdateService.cs +++ b/src/Web/Services/Implementations/UpdateService.cs @@ -1,6 +1,6 @@ -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Services.Implementations; +namespace PoEGamblingHelper.Web.Services.Implementations; public class UpdateService : IUpdateService { diff --git a/src/Web/Services/Interfaces/HttpService.cs b/src/Web/Services/Interfaces/HttpService.cs index 84afe914..c0643682 100644 --- a/src/Web/Services/Interfaces/HttpService.cs +++ b/src/Web/Services/Interfaces/HttpService.cs @@ -1,9 +1,8 @@ using System.Net.Http.Json; using Blazored.Toast.Services; -using Domain.Exception.Body; -using Web.Util; +using PoEGamblingHelper.Web.Util; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public abstract class HttpService { diff --git a/src/Web/Services/Interfaces/ICurrencyService.cs b/src/Web/Services/Interfaces/ICurrencyService.cs index b9d0734a..707275d7 100644 --- a/src/Web/Services/Interfaces/ICurrencyService.cs +++ b/src/Web/Services/Interfaces/ICurrencyService.cs @@ -1,6 +1,6 @@ using PoEGamblingHelper.Domain.Entity; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface ICurrencyService { diff --git a/src/Web/Services/Interfaces/IGemService.cs b/src/Web/Services/Interfaces/IGemService.cs index 7afc461e..600fa409 100644 --- a/src/Web/Services/Interfaces/IGemService.cs +++ b/src/Web/Services/Interfaces/IGemService.cs @@ -1,6 +1,7 @@ using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Shared.QueryParameters; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface IGemService { diff --git a/src/Web/Services/Interfaces/ILeagueService.cs b/src/Web/Services/Interfaces/ILeagueService.cs index 198cda59..6750b67f 100644 --- a/src/Web/Services/Interfaces/ILeagueService.cs +++ b/src/Web/Services/Interfaces/ILeagueService.cs @@ -1,6 +1,6 @@ using PoEGamblingHelper.Domain.Entity; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface ILeagueService { diff --git a/src/Web/Services/Interfaces/IScrollInfoService.cs b/src/Web/Services/Interfaces/IScrollInfoService.cs index d395a651..ee460752 100644 --- a/src/Web/Services/Interfaces/IScrollInfoService.cs +++ b/src/Web/Services/Interfaces/IScrollInfoService.cs @@ -1,4 +1,4 @@ -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface IScrollInfoService { diff --git a/src/Web/Services/Interfaces/ITempleCostService.cs b/src/Web/Services/Interfaces/ITempleCostService.cs index 5aa86c77..68afa0c7 100644 --- a/src/Web/Services/Interfaces/ITempleCostService.cs +++ b/src/Web/Services/Interfaces/ITempleCostService.cs @@ -1,6 +1,6 @@ using PoEGamblingHelper.Domain.Entity; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface ITempleCostService { diff --git a/src/Web/Services/Interfaces/IUpdateService.cs b/src/Web/Services/Interfaces/IUpdateService.cs index edfe6a56..bef04171 100644 --- a/src/Web/Services/Interfaces/IUpdateService.cs +++ b/src/Web/Services/Interfaces/IUpdateService.cs @@ -1,6 +1,6 @@ -using Web.Services.Implementations; +using PoEGamblingHelper.Web.Services.Implementations; -namespace Web.Services.Interfaces; +namespace PoEGamblingHelper.Web.Services.Interfaces; public interface IUpdateService : IAsyncDisposable { diff --git a/src/Web/Shared/Components/Filter.razor.cs b/src/Web/Shared/Components/Filter.razor.cs index f787c628..8e012edf 100644 --- a/src/Web/Shared/Components/Filter.razor.cs +++ b/src/Web/Shared/Components/Filter.razor.cs @@ -2,10 +2,11 @@ using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using PoEGamblingHelper.Domain.Entity; -using Web.Shared.Model; -using Web.Util; +using PoEGamblingHelper.Shared.QueryParameters; +using PoEGamblingHelper.Web.Shared.Model; +using PoEGamblingHelper.Web.Util; -namespace Web.Shared.Components; +namespace PoEGamblingHelper.Web.Shared.Components; public partial class Filter : ComponentBase { diff --git a/src/Web/Shared/Components/Footer.razor.cs b/src/Web/Shared/Components/Footer.razor.cs index 310832db..d24111cb 100644 --- a/src/Web/Shared/Components/Footer.razor.cs +++ b/src/Web/Shared/Components/Footer.razor.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Components; -using Web.Services.Interfaces; +using PoEGamblingHelper.Web.Services.Interfaces; -namespace Web.Shared.Components; +namespace PoEGamblingHelper.Web.Shared.Components; public partial class Footer : IAsyncDisposable { diff --git a/src/Web/Shared/Components/GemStats.razor.cs b/src/Web/Shared/Components/GemStats.razor.cs index ec83d7fa..c7603a4d 100644 --- a/src/Web/Shared/Components/GemStats.razor.cs +++ b/src/Web/Shared/Components/GemStats.razor.cs @@ -1,12 +1,13 @@ using System.Diagnostics; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; -using Web.Shared.Model; -using Web.Util; +using PoEGamblingHelper.Web.Shared.Model; +using PoEGamblingHelper.Web.Util; -namespace Web.Shared.Components; +namespace PoEGamblingHelper.Web.Shared.Components; public partial class GemStats { diff --git a/src/Web/Shared/Components/Theme.razor.cs b/src/Web/Shared/Components/Theme.razor.cs index 8120ac7a..a3e3202c 100644 --- a/src/Web/Shared/Components/Theme.razor.cs +++ b/src/Web/Shared/Components/Theme.razor.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; -namespace Web.Shared.Components; +namespace PoEGamblingHelper.Web.Shared.Components; public partial class Theme { diff --git a/src/Web/Shared/Model/FilterValues.cs b/src/Web/Shared/Model/FilterValues.cs index bacd1029..44e5d85a 100644 --- a/src/Web/Shared/Model/FilterValues.cs +++ b/src/Web/Shared/Model/FilterValues.cs @@ -1,6 +1,7 @@ using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Shared.QueryParameters; -namespace Web.Shared.Model; +namespace PoEGamblingHelper.Web.Shared.Model; public class FilterValues { diff --git a/src/Web/Util/ExtensionMethods.cs b/src/Web/Util/ExtensionMethods.cs index 5d2df4e7..6b938dea 100644 --- a/src/Web/Util/ExtensionMethods.cs +++ b/src/Web/Util/ExtensionMethods.cs @@ -2,10 +2,12 @@ using System.Net.Http.Json; using System.Text.RegularExpressions; using System.Web; +using PoEGamblingHelper.Application.Exception.Body; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Shared.QueryParameters; -namespace Web.Util; +namespace PoEGamblingHelper.Web.Util; public static partial class ExtensionMethods { diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 4b04101f..c2f28386 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -14,7 +14,9 @@ + + diff --git a/test/Web.Test/Shared/Model/FilterValuesTest.cs b/test/Web.Test/Shared/Model/FilterValuesTest.cs index f736a74e..39c7109b 100644 --- a/test/Web.Test/Shared/Model/FilterValuesTest.cs +++ b/test/Web.Test/Shared/Model/FilterValuesTest.cs @@ -1,4 +1,4 @@ -using Web.Shared.Model; +using PoEGamblingHelper.Web.Shared.Model; namespace Web.Test.Shared.Model; diff --git a/test/Web.Test/Util/ExtensionFunctionsTest.cs b/test/Web.Test/Util/ExtensionFunctionsTest.cs index 2458f008..7dd0caa5 100644 --- a/test/Web.Test/Util/ExtensionFunctionsTest.cs +++ b/test/Web.Test/Util/ExtensionFunctionsTest.cs @@ -1,6 +1,6 @@ using System.Globalization; using FluentAssertions; -using Web.Util; +using PoEGamblingHelper.Web.Util; using Xunit.Abstractions; namespace Web.Test.Util; From 8810ee81b613dd40ce78f0d367192add130bf6c8 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 14:34:39 +0200 Subject: [PATCH 023/129] fix stuff --- PoEGamblingHelper.sln | 6 ------ src/Application/Extensions/GemDataExtensions.cs | 4 ++-- .../QueryParameters/GemDataQuery.cs | 2 +- src/{Shared => Application}/QueryParameters/GemType.cs | 2 +- src/{Shared => Application}/QueryParameters/Page.cs | 2 +- src/{Shared => Application}/QueryParameters/PageRequest.cs | 2 +- src/{Shared => Application}/QueryParameters/Sort.cs | 2 +- src/Application/Services/IGemService.cs | 3 ++- src/Application/Util/ExtensionMethods.cs | 3 ++- .../Migrations/20230216010517_1.0.Designer.cs | 2 +- .../Migrations/20230309081719_2.0.Designer.cs | 2 +- .../Migrations/20230419193237_1.1.Designer.cs | 2 +- .../Migrations/20230420132741_1.2.Designer.cs | 2 +- .../Migrations/ApplicationDbContextModelSnapshot.cs | 2 +- src/Infrastructure/Services/DataFetchService.cs | 1 + src/{Application => Infrastructure}/Services/GemService.cs | 6 +++++- src/Shared/Shared.csproj | 7 ------- src/Web/Pages/GamblingHelper.razor.cs | 1 - src/Web/Services/Implementations/GemService.cs | 2 +- src/Web/Services/Interfaces/IGemService.cs | 2 +- src/Web/Shared/Components/Filter.razor.cs | 2 +- src/Web/Shared/Model/FilterValues.cs | 4 ++-- src/Web/Util/ExtensionMethods.cs | 2 +- src/Web/Web.csproj | 1 - test/Application.Test/Services/GemServiceTest.cs | 3 +-- .../Util/ExtensionMethodsTest.cs | 6 ++++-- 26 files changed, 33 insertions(+), 40 deletions(-) rename src/{Shared => Application}/QueryParameters/GemDataQuery.cs (89%) rename src/{Shared => Application}/QueryParameters/GemType.cs (68%) rename src/{Shared => Application}/QueryParameters/Page.cs (76%) rename src/{Shared => Application}/QueryParameters/PageRequest.cs (81%) rename src/{Shared => Application}/QueryParameters/Sort.cs (77%) rename src/{Application => Infrastructure}/Services/GemService.cs (95%) delete mode 100644 src/Shared/Shared.csproj rename test/{Domain.Test => Application.Test}/Util/ExtensionMethodsTest.cs (62%) diff --git a/PoEGamblingHelper.sln b/PoEGamblingHelper.sln index cbe364bb..7678fc08 100644 --- a/PoEGamblingHelper.sln +++ b/PoEGamblingHelper.sln @@ -18,8 +18,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Test", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Test", "test\Web.Test\Web.Test.csproj", "{72665095-42B7-4E4A-8019-1B6EF259C31B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{518594C7-55A5-4D4D-B771-01457BA8B17A}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -62,9 +60,5 @@ Global {72665095-42B7-4E4A-8019-1B6EF259C31B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72665095-42B7-4E4A-8019-1B6EF259C31B}.Release|Any CPU.Build.0 = Release|Any CPU - {518594C7-55A5-4D4D-B771-01457BA8B17A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {518594C7-55A5-4D4D-B771-01457BA8B17A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {518594C7-55A5-4D4D-B771-01457BA8B17A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {518594C7-55A5-4D4D-B771-01457BA8B17A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index 2dbeec5e..4195a2de 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -1,5 +1,5 @@ -using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Domain.Util; +using PoEGamblingHelper.Application.Util; +using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Application.Extensions; diff --git a/src/Shared/QueryParameters/GemDataQuery.cs b/src/Application/QueryParameters/GemDataQuery.cs similarity index 89% rename from src/Shared/QueryParameters/GemDataQuery.cs rename to src/Application/QueryParameters/GemDataQuery.cs index f9c2b610..a565dbab 100644 --- a/src/Shared/QueryParameters/GemDataQuery.cs +++ b/src/Application/QueryParameters/GemDataQuery.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Shared.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters { public class GemDataQuery { diff --git a/src/Shared/QueryParameters/GemType.cs b/src/Application/QueryParameters/GemType.cs similarity index 68% rename from src/Shared/QueryParameters/GemType.cs rename to src/Application/QueryParameters/GemType.cs index 9b5af493..3d908dd8 100644 --- a/src/Shared/QueryParameters/GemType.cs +++ b/src/Application/QueryParameters/GemType.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Shared.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters { public enum GemType { diff --git a/src/Shared/QueryParameters/Page.cs b/src/Application/QueryParameters/Page.cs similarity index 76% rename from src/Shared/QueryParameters/Page.cs rename to src/Application/QueryParameters/Page.cs index 2da7520d..06dfe193 100644 --- a/src/Shared/QueryParameters/Page.cs +++ b/src/Application/QueryParameters/Page.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Shared.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters { public class Page { diff --git a/src/Shared/QueryParameters/PageRequest.cs b/src/Application/QueryParameters/PageRequest.cs similarity index 81% rename from src/Shared/QueryParameters/PageRequest.cs rename to src/Application/QueryParameters/PageRequest.cs index 1fc049fa..1ef40593 100644 --- a/src/Shared/QueryParameters/PageRequest.cs +++ b/src/Application/QueryParameters/PageRequest.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Shared.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters { public class PageRequest { diff --git a/src/Shared/QueryParameters/Sort.cs b/src/Application/QueryParameters/Sort.cs similarity index 77% rename from src/Shared/QueryParameters/Sort.cs rename to src/Application/QueryParameters/Sort.cs index e364b370..2ce06d01 100644 --- a/src/Shared/QueryParameters/Sort.cs +++ b/src/Application/QueryParameters/Sort.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Shared.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters { public enum Sort { diff --git a/src/Application/Services/IGemService.cs b/src/Application/Services/IGemService.cs index 4eb47ba4..84b6ee5b 100644 --- a/src/Application/Services/IGemService.cs +++ b/src/Application/Services/IGemService.cs @@ -1,4 +1,5 @@ -using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Application.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Application.Services; diff --git a/src/Application/Util/ExtensionMethods.cs b/src/Application/Util/ExtensionMethods.cs index 7e214b09..b7b330b4 100644 --- a/src/Application/Util/ExtensionMethods.cs +++ b/src/Application/Util/ExtensionMethods.cs @@ -1,4 +1,5 @@ -using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Application.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Application.Util; diff --git a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs b/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs index d0b89c30..59c1018a 100644 --- a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs +++ b/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs index 7cb33420..fb5dd8b9 100644 --- a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs +++ b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs b/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs index c58f70c0..e9029170 100644 --- a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs +++ b/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs b/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs index 39f92733..4d370f03 100644 --- a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs +++ b/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index bcf7d220..7aa69fc2 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,6 +1,6 @@ // using System; -using Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; diff --git a/src/Infrastructure/Services/DataFetchService.cs b/src/Infrastructure/Services/DataFetchService.cs index c8e53077..015555e3 100644 --- a/src/Infrastructure/Services/DataFetchService.cs +++ b/src/Infrastructure/Services/DataFetchService.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Database; diff --git a/src/Application/Services/GemService.cs b/src/Infrastructure/Services/GemService.cs similarity index 95% rename from src/Application/Services/GemService.cs rename to src/Infrastructure/Services/GemService.cs index 97fa44aa..f5589a1a 100644 --- a/src/Application/Services/GemService.cs +++ b/src/Infrastructure/Services/GemService.cs @@ -1,9 +1,13 @@ using System.Diagnostics; using System.Text.RegularExpressions; +using PoEGamblingHelper.Application.Extensions; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Application.Util; using PoEGamblingHelper.Domain.Entity.Gem; -namespace PoEGamblingHelper.Application.Services; +namespace PoEGamblingHelper.Infrastructure.Services; public partial class GemService : IGemService { diff --git a/src/Shared/Shared.csproj b/src/Shared/Shared.csproj deleted file mode 100644 index e183d57d..00000000 --- a/src/Shared/Shared.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.1 - - - diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 6e623ae3..ef286ce7 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -3,7 +3,6 @@ using Microsoft.JSInterop; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Shared.QueryParameters; using PoEGamblingHelper.Web.Services.Interfaces; using PoEGamblingHelper.Web.Shared.Model; diff --git a/src/Web/Services/Implementations/GemService.cs b/src/Web/Services/Implementations/GemService.cs index d9c33cfc..5ea10440 100644 --- a/src/Web/Services/Implementations/GemService.cs +++ b/src/Web/Services/Implementations/GemService.cs @@ -1,6 +1,6 @@ using Blazored.Toast.Services; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Shared.QueryParameters; using PoEGamblingHelper.Web.Services.Interfaces; namespace PoEGamblingHelper.Web.Services.Implementations; diff --git a/src/Web/Services/Interfaces/IGemService.cs b/src/Web/Services/Interfaces/IGemService.cs index 600fa409..c4b1fd8a 100644 --- a/src/Web/Services/Interfaces/IGemService.cs +++ b/src/Web/Services/Interfaces/IGemService.cs @@ -1,5 +1,5 @@ +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Shared.QueryParameters; namespace PoEGamblingHelper.Web.Services.Interfaces; diff --git a/src/Web/Shared/Components/Filter.razor.cs b/src/Web/Shared/Components/Filter.razor.cs index 8e012edf..ccc3a0b2 100644 --- a/src/Web/Shared/Components/Filter.razor.cs +++ b/src/Web/Shared/Components/Filter.razor.cs @@ -1,8 +1,8 @@ using System.Text.RegularExpressions; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Shared.QueryParameters; using PoEGamblingHelper.Web.Shared.Model; using PoEGamblingHelper.Web.Util; diff --git a/src/Web/Shared/Model/FilterValues.cs b/src/Web/Shared/Model/FilterValues.cs index 44e5d85a..e8727cf2 100644 --- a/src/Web/Shared/Model/FilterValues.cs +++ b/src/Web/Shared/Model/FilterValues.cs @@ -1,5 +1,5 @@ -using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Shared.QueryParameters; +using PoEGamblingHelper.Application.QueryParameters; +using PoEGamblingHelper.Domain.Entity; namespace PoEGamblingHelper.Web.Shared.Model; diff --git a/src/Web/Util/ExtensionMethods.cs b/src/Web/Util/ExtensionMethods.cs index 6b938dea..e2a21bd3 100644 --- a/src/Web/Util/ExtensionMethods.cs +++ b/src/Web/Util/ExtensionMethods.cs @@ -3,9 +3,9 @@ using System.Text.RegularExpressions; using System.Web; using PoEGamblingHelper.Application.Exception.Body; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Shared.QueryParameters; namespace PoEGamblingHelper.Web.Util; diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index c2f28386..96b0a776 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -16,7 +16,6 @@ - diff --git a/test/Application.Test/Services/GemServiceTest.cs b/test/Application.Test/Services/GemServiceTest.cs index ae13c297..97b0d867 100644 --- a/test/Application.Test/Services/GemServiceTest.cs +++ b/test/Application.Test/Services/GemServiceTest.cs @@ -1,9 +1,8 @@ using MockQueryable.Moq; using Moq; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity.Gem; -namespace Application.Test.Services; +namespace PoEGamblingHelper.Application.Test.Services; public class GemServiceTest { diff --git a/test/Domain.Test/Util/ExtensionMethodsTest.cs b/test/Application.Test/Util/ExtensionMethodsTest.cs similarity index 62% rename from test/Domain.Test/Util/ExtensionMethodsTest.cs rename to test/Application.Test/Util/ExtensionMethodsTest.cs index 9388fc78..468ed1c5 100644 --- a/test/Domain.Test/Util/ExtensionMethodsTest.cs +++ b/test/Application.Test/Util/ExtensionMethodsTest.cs @@ -1,6 +1,8 @@ -using PoEGamblingHelper.Domain.Entity.Gem; +using FluentAssertions; +using PoEGamblingHelper.Application.Util; +using PoEGamblingHelper.Domain.Entity.Gem; -namespace Domain.Test.Util; +namespace PoEGamblingHelper.Application.Test.Util; public class ExtensionMethodsTest { From b552f4cc3e244e26f0d5a64cc73203d8e43c8e1e Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 14:39:52 +0200 Subject: [PATCH 024/129] fix Application --- src/Application/Exception/Body/ExceptionId.cs | 2 +- .../Extensions/GemDataExtensions.cs | 20 ++++++++-------- .../QueryParameters/GemDataQuery.cs | 23 +++++++++---------- src/Application/QueryParameters/GemType.cs | 17 +++++++------- src/Application/QueryParameters/Page.cs | 13 +++++------ .../QueryParameters/PageRequest.cs | 19 ++++++++------- src/Application/QueryParameters/Sort.cs | 19 ++++++++------- src/Application/Services/AnalyticsService.cs | 1 + src/Application/Services/IAnalyticsService.cs | 2 +- src/Application/Services/IDataFetchService.cs | 2 +- src/Application/Services/IGemService.cs | 2 +- src/Application/Services/ILeagueService.cs | 2 +- src/Application/Services/LeagueService.cs | 2 +- src/Domain/Entity/Gem/GemData.cs | 2 +- 14 files changed, 61 insertions(+), 65 deletions(-) diff --git a/src/Application/Exception/Body/ExceptionId.cs b/src/Application/Exception/Body/ExceptionId.cs index 89a13779..f9ca1cf1 100644 --- a/src/Application/Exception/Body/ExceptionId.cs +++ b/src/Application/Exception/Body/ExceptionId.cs @@ -2,7 +2,7 @@ namespace PoEGamblingHelper.Application.Exception.Body; -public static class ExceptionIdExtensions +public static class ExceptionIdExtensions //TODO what is this { public static string ToIdString(this ExceptionId exceptionId) { diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index 4195a2de..6bcf9695 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -32,20 +32,20 @@ public static decimal Profit(this GemData gemData, return gemData.Profit(gemData.Value(resultCase), rawCost, templeCost); } - public static decimal Profit(this GemData gemData, - decimal value, - decimal? rawCost = null, - decimal templeCost = 0) + private static decimal Profit(this GemData gemData, + decimal value, + decimal? rawCost = null, + decimal templeCost = 0) { return value - gemData.CostPerTry(rawCost, templeCost); } - public static decimal Value(this GemData gemData, ResultCase resultCase) + private static decimal Value(this GemData gemData, ResultCase resultCase) { return gemData.ResultValue(gemData.MaxLevel() + resultCase.LevelModifier()); } - internal static decimal ResultValue(this GemData gemData, int level) + private static decimal ResultValue(this GemData gemData, int level) { return gemData.Gems.Where(gem => gem.GemLevel == level && gem.Corrupted).MinBy(gem => gem.GemQuality) ?.ChaosValue ?? 0m; @@ -61,15 +61,15 @@ public static int MaxLevel(this GemData gemData) 20; } - internal static bool IsExceptional(this GemData gemData) + private static bool IsExceptional(this GemData gemData) { var lowerName = gemData.Name.ToLowerInvariant(); return lowerName.Contains("enhance") || lowerName.Contains("empower") || lowerName.Contains("enlighten"); } - public static decimal CostPerTry(this GemData gemData, - decimal? rawCost = null, - decimal templeCost = 0) + private static decimal CostPerTry(this GemData gemData, + decimal? rawCost = null, + decimal templeCost = 0) { return (rawCost ?? gemData.RawCost()) + templeCost; } diff --git a/src/Application/QueryParameters/GemDataQuery.cs b/src/Application/QueryParameters/GemDataQuery.cs index a565dbab..cfd4bd45 100644 --- a/src/Application/QueryParameters/GemDataQuery.cs +++ b/src/Application/QueryParameters/GemDataQuery.cs @@ -1,14 +1,13 @@ -namespace PoEGamblingHelper.Application.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters; + +public class GemDataQuery { - public class GemDataQuery - { - public string SearchText { get; set; } = string.Empty; - public Sort Sort { get; init; } = Sort.AverageProfitPerTryDesc; - public GemType GemType { get; init; } = GemType.All; - public bool OnlyShowProfitable { get; init; } - public bool ShowAlternateQuality { get; init; } - public bool ShowVaal { get; set; } = false; - public decimal? PricePerTryFrom { get; set; } - public decimal? PricePerTryTo { get; set; } - } + public string SearchText { get; set; } = string.Empty; + public Sort Sort { get; init; } = Sort.AverageProfitPerTryDesc; + public GemType GemType { get; init; } = GemType.All; + public bool OnlyShowProfitable { get; init; } + public bool ShowAlternateQuality { get; init; } + public bool ShowVaal { get; set; } = false; + public decimal? PricePerTryFrom { get; set; } + public decimal? PricePerTryTo { get; set; } } \ No newline at end of file diff --git a/src/Application/QueryParameters/GemType.cs b/src/Application/QueryParameters/GemType.cs index 3d908dd8..850c3382 100644 --- a/src/Application/QueryParameters/GemType.cs +++ b/src/Application/QueryParameters/GemType.cs @@ -1,11 +1,10 @@ -namespace PoEGamblingHelper.Application.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters; + +public enum GemType { - public enum GemType - { - All, - Exceptional, - Awakened, - RegularSupport, - Skill - } + All, + Exceptional, + Awakened, + RegularSupport, + Skill } \ No newline at end of file diff --git a/src/Application/QueryParameters/Page.cs b/src/Application/QueryParameters/Page.cs index 06dfe193..a51f2ae4 100644 --- a/src/Application/QueryParameters/Page.cs +++ b/src/Application/QueryParameters/Page.cs @@ -1,9 +1,8 @@ -namespace PoEGamblingHelper.Application.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters; + +public class Page { - public class Page - { - public IEnumerable Content { get; init; } = null!; - public int CurrentPage { get; init; } - public bool LastPage { get; init; } - } + public IEnumerable Content { get; init; } = null!; + public int CurrentPage { get; init; } + public bool LastPage { get; init; } } \ No newline at end of file diff --git a/src/Application/QueryParameters/PageRequest.cs b/src/Application/QueryParameters/PageRequest.cs index 1ef40593..36066cbe 100644 --- a/src/Application/QueryParameters/PageRequest.cs +++ b/src/Application/QueryParameters/PageRequest.cs @@ -1,14 +1,13 @@ -namespace PoEGamblingHelper.Application.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters; + +public class PageRequest { - public class PageRequest - { - private int _pageSize; - public int PageNumber { get; set; } + private int _pageSize; + public int PageNumber { get; set; } - public int PageSize - { - get => _pageSize; - set => _pageSize = value <= 0 ? 0 : value; - } + public int PageSize + { + get => _pageSize; + set => _pageSize = value <= 0 ? 0 : value; } } \ No newline at end of file diff --git a/src/Application/QueryParameters/Sort.cs b/src/Application/QueryParameters/Sort.cs index 2ce06d01..ca1b8396 100644 --- a/src/Application/QueryParameters/Sort.cs +++ b/src/Application/QueryParameters/Sort.cs @@ -1,12 +1,11 @@ -namespace PoEGamblingHelper.Application.QueryParameters +namespace PoEGamblingHelper.Application.QueryParameters; + +public enum Sort { - public enum Sort - { - CostPerTryAsc, - CostPerTryDesc, - AverageProfitPerTryAsc, - AverageProfitPerTryDesc, - MaxProfitPerTryAsc, - MaxProfitPerTryDesc - } + CostPerTryAsc, + CostPerTryDesc, + AverageProfitPerTryAsc, + AverageProfitPerTryDesc, + MaxProfitPerTryAsc, + MaxProfitPerTryDesc } \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index f8dbe230..3c23f74c 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -37,4 +37,5 @@ public async Task LogYesterdaysViews() // ctx.View.RemoveRange(views); // await ctx.SaveChangesAsync(); } + //TODO fix this } \ No newline at end of file diff --git a/src/Application/Services/IAnalyticsService.cs b/src/Application/Services/IAnalyticsService.cs index 92269c6f..2fd35187 100644 --- a/src/Application/Services/IAnalyticsService.cs +++ b/src/Application/Services/IAnalyticsService.cs @@ -1,6 +1,6 @@ namespace PoEGamblingHelper.Application.Services; -public interface IAnalyticsService +public interface IAnalyticsService //TODO rename { Task AddView(string? ipAddress); Task LogYesterdaysViews(); diff --git a/src/Application/Services/IDataFetchService.cs b/src/Application/Services/IDataFetchService.cs index 96c255a5..7c1e201e 100644 --- a/src/Application/Services/IDataFetchService.cs +++ b/src/Application/Services/IDataFetchService.cs @@ -2,7 +2,7 @@ namespace PoEGamblingHelper.Application.Services; -public interface IDataFetchService +public interface IDataFetchService //TODO rename { Task FetchCurrentLeague(); Task FetchCurrencyData(League league); diff --git a/src/Application/Services/IGemService.cs b/src/Application/Services/IGemService.cs index 84b6ee5b..c7a39996 100644 --- a/src/Application/Services/IGemService.cs +++ b/src/Application/Services/IGemService.cs @@ -3,7 +3,7 @@ namespace PoEGamblingHelper.Application.Services; -public interface IGemService +public interface IGemService //TODO rename { Task> GetAll(GemDataQuery? query, PageRequest page); } \ No newline at end of file diff --git a/src/Application/Services/ILeagueService.cs b/src/Application/Services/ILeagueService.cs index c63ed91f..7bc63afd 100644 --- a/src/Application/Services/ILeagueService.cs +++ b/src/Application/Services/ILeagueService.cs @@ -2,7 +2,7 @@ namespace PoEGamblingHelper.Application.Services; -public interface ILeagueService +public interface ILeagueService //TODO rename { League GetCurrentLeague(); } \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs index 27360155..8522b582 100644 --- a/src/Application/Services/LeagueService.cs +++ b/src/Application/Services/LeagueService.cs @@ -16,5 +16,5 @@ public League GetCurrentLeague() // .OrderByDescending(league => league.StartDate) // .FirstOrDefault() // ?? throw new NoLeagueDataException(); - } + } //TODO fix this } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/GemData.cs b/src/Domain/Entity/Gem/GemData.cs index d430d0e0..a8a0ffb0 100644 --- a/src/Domain/Entity/Gem/GemData.cs +++ b/src/Domain/Entity/Gem/GemData.cs @@ -11,6 +11,6 @@ public class GemData : Entity { public string Name { get; set; } = string.Empty; public string Icon { get; set; } = string.Empty; - public ICollection Gems { get; set; } = new List(); + public IList Gems { get; set; } = new List(); } } \ No newline at end of file From 3815772bc962dcede3d4093782f872a3ed6addec Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 14:54:09 +0200 Subject: [PATCH 025/129] cleanup --- src/Api/AnalyticsMiddleware.cs | 3 +- src/Api/Api.csproj | 1 + src/Api/{ => Configuration}/ConfigureCache.cs | 2 +- .../ConfigureRateLimiter.cs | 2 +- src/Api/Controllers/CurrencyController.cs | 5 +- src/Api/Controllers/GemController.cs | 2 + src/Api/Controllers/LeagueController.cs | 18 +++---- src/Api/Controllers/StatsController.cs | 47 ++++++++----------- src/Api/Controllers/TempleController.cs | 14 +++--- .../HttpExtensions.cs} | 4 +- ...lter.cs => HttpExceptionResponseFilter.cs} | 2 +- src/Api/Program.cs | 29 ++++++------ .../Repositories/ILeagueRepository.cs | 1 + .../Repositories/ITempleRepository.cs | 8 ++++ src/Application/Services/ILeagueService.cs | 1 + src/Application/Services/LeagueService.cs | 10 ++-- .../Repositories/LeagueRepository.cs | 33 +++++++++++++ .../Repositories/TempleRepository.cs | 24 ++++++++++ src/Infrastructure/Services/GemService.cs | 14 ++++-- 19 files changed, 140 insertions(+), 80 deletions(-) rename src/Api/{ => Configuration}/ConfigureCache.cs (93%) rename src/Api/{ => Configuration}/ConfigureRateLimiter.cs (98%) rename src/Api/{Controllers/HttpUtilFunctions.cs => Extensions/HttpExtensions.cs} (81%) rename src/Api/Filters/{HttpResponseExceptionFilter.cs => HttpExceptionResponseFilter.cs} (91%) create mode 100644 src/Application/Repositories/ITempleRepository.cs create mode 100644 src/Infrastructure/Repositories/LeagueRepository.cs create mode 100644 src/Infrastructure/Repositories/TempleRepository.cs diff --git a/src/Api/AnalyticsMiddleware.cs b/src/Api/AnalyticsMiddleware.cs index 1ba3b259..1f105205 100644 --- a/src/Api/AnalyticsMiddleware.cs +++ b/src/Api/AnalyticsMiddleware.cs @@ -1,4 +1,4 @@ -using PoEGamblingHelper.Api.Controllers; +using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Services; namespace PoEGamblingHelper.Api; @@ -12,6 +12,7 @@ public class AnalyticsMiddleware // IMessageWriter is injected into InvokeAsync public async Task InvokeAsync(HttpContext httpContext, IAnalyticsService analyticsService) { + Console.WriteLine("CALL INVOKE"); //TODO await analyticsService.AddView(httpContext.Request.GetRealIpAddress()); await _next(httpContext); } diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index 5c4b6fa2..fb28f65d 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -21,4 +21,5 @@ + diff --git a/src/Api/ConfigureCache.cs b/src/Api/Configuration/ConfigureCache.cs similarity index 93% rename from src/Api/ConfigureCache.cs rename to src/Api/Configuration/ConfigureCache.cs index 1211ba0b..6b3119fa 100644 --- a/src/Api/ConfigureCache.cs +++ b/src/Api/Configuration/ConfigureCache.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Api; +namespace PoEGamblingHelper.Api.Configuration; public static class ConfigureCache { diff --git a/src/Api/ConfigureRateLimiter.cs b/src/Api/Configuration/ConfigureRateLimiter.cs similarity index 98% rename from src/Api/ConfigureRateLimiter.cs rename to src/Api/Configuration/ConfigureRateLimiter.cs index b19c4b14..ebb77b30 100644 --- a/src/Api/ConfigureRateLimiter.cs +++ b/src/Api/Configuration/ConfigureRateLimiter.cs @@ -2,7 +2,7 @@ using System.Net; using System.Threading.RateLimiting; -namespace PoEGamblingHelper.Api; +namespace PoEGamblingHelper.Api.Configuration; public static class ConfigureRateLimiter { diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 351bd998..64248cd6 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Repositories; @@ -20,9 +21,9 @@ public CurrencyController(IAnalyticsService analyticsService, [HttpGet] [OutputCache(PolicyName = "FetchData")] - public async IAsyncEnumerable GetAll() + public IAsyncEnumerable GetAll() //TODO { - await _analyticsService.AddView(Request.GetRealIpAddress()); + _analyticsService.AddView(Request.GetRealIpAddress()).RunSynchronously(); return _currencyRepository.GetAll(); } } \ No newline at end of file diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index 3e64b4b1..3a2587bb 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Extensions; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity.Gem; diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index 188a3616..3cf17d06 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; @@ -8,25 +9,19 @@ namespace PoEGamblingHelper.Api.Controllers; public class LeagueController : ApiControllerBase { private readonly IAnalyticsService _analyticsService; - private readonly IApplicationDbContextFactory _applicationDbContextFactory; private readonly ILeagueService _leagueService; - public LeagueController(ILeagueService leagueService, - IApplicationDbContextFactory applicationDbContextFactory, - IAnalyticsService analyticsService) + public LeagueController(ILeagueService leagueService, IAnalyticsService analyticsService) { _leagueService = leagueService; - _applicationDbContextFactory = applicationDbContextFactory; _analyticsService = analyticsService; } [HttpGet] - public async IAsyncEnumerable GetAllLeagues() + public IAsyncEnumerable GetAllLeagues() //TODO { - await _analyticsService.AddView(Request.GetRealIpAddress()); - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - await foreach (var item in applicationDbContext.League.AsAsyncEnumerable().ConfigureAwait(false)) - yield return item; + _analyticsService.AddView(Request.GetRealIpAddress()).RunSynchronously(); + return _leagueService.GetAllLeagues(); } [HttpGet] @@ -35,7 +30,6 @@ public async IAsyncEnumerable GetAllLeagues() public League GetCurrentLeague() { _analyticsService.AddView(Request.GetRealIpAddress()); - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - return _leagueService.GetCurrentLeague(applicationDbContext.League); + return _leagueService.GetCurrentLeague(); } } \ No newline at end of file diff --git a/src/Api/Controllers/StatsController.cs b/src/Api/Controllers/StatsController.cs index 43cd1087..16250ad8 100644 --- a/src/Api/Controllers/StatsController.cs +++ b/src/Api/Controllers/StatsController.cs @@ -1,34 +1,25 @@ -using Microsoft.AspNetCore.Mvc; -using PoEGamblingHelper.Domain.Entity.Stats; - namespace PoEGamblingHelper.Api.Controllers; public class StatsController : ApiControllerBase -{ - private readonly IApplicationDbContextFactory _applicationDbContextFactory; - - public StatsController(IApplicationDbContextFactory applicationDbContextFactory) - { - _applicationDbContextFactory = applicationDbContextFactory; - } +{ //TODO - [HttpGet] - public async IAsyncEnumerable GetAll() - { - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - await foreach (var item in applicationDbContext.Result - .Include(r => r.CurrencyResult) - .Include(r => r.GemTradeData) - .AsAsyncEnumerable() - .ConfigureAwait(false)) - yield return item; - } + // [HttpGet] + // public async IAsyncEnumerable GetAll() + // { + // using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + // await foreach (var item in applicationDbContext.Result + // .Include(r => r.CurrencyResult) + // .Include(r => r.GemTradeData) + // .AsAsyncEnumerable() + // .ConfigureAwait(false)) + // yield return item; + // } - [HttpPost] - public async Task Post(Result result) - { - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - await applicationDbContext.Result.AddAsync(result); - await applicationDbContext.SaveChangesAsync(); - } + // [HttpPost] + // public async Task Post(Result result) + // { + // using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + // await applicationDbContext.Result.AddAsync(result); + // await applicationDbContext.SaveChangesAsync(); + // } } \ No newline at end of file diff --git a/src/Api/Controllers/TempleController.cs b/src/Api/Controllers/TempleController.cs index 091a98f0..c3b8a9a3 100644 --- a/src/Api/Controllers/TempleController.cs +++ b/src/Api/Controllers/TempleController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Api.Extensions; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; @@ -9,13 +10,12 @@ namespace PoEGamblingHelper.Api.Controllers; public class TempleController : ApiControllerBase { private readonly IAnalyticsService _analyticsService; - private readonly IApplicationDbContextFactory _applicationDbContextFactory; + private readonly ITempleRepository _templeRepository; - public TempleController(IApplicationDbContextFactory applicationDbContextFactory, - IAnalyticsService analyticsService) + public TempleController(IAnalyticsService analyticsService, ITempleRepository templeRepository) { - _applicationDbContextFactory = applicationDbContextFactory; _analyticsService = analyticsService; + _templeRepository = templeRepository; } [HttpGet] @@ -23,8 +23,6 @@ public TempleController(IApplicationDbContextFactory applicationDbContextFactory public TempleCost Get() { _analyticsService.AddView(Request.GetRealIpAddress()); - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); - return applicationDbContext.TempleCost.OrderByDescending(cost => cost.TimeStamp).FirstOrDefault() - ?? throw new NoTempleDataException(); + return _templeRepository.GetCurrent(); } } \ No newline at end of file diff --git a/src/Api/Controllers/HttpUtilFunctions.cs b/src/Api/Extensions/HttpExtensions.cs similarity index 81% rename from src/Api/Controllers/HttpUtilFunctions.cs rename to src/Api/Extensions/HttpExtensions.cs index 64248671..6332826f 100644 --- a/src/Api/Controllers/HttpUtilFunctions.cs +++ b/src/Api/Extensions/HttpExtensions.cs @@ -1,6 +1,6 @@ -namespace PoEGamblingHelper.Api.Controllers; +namespace PoEGamblingHelper.Api.Extensions; -public static class HttpUtilFunctions +public static class HttpExtensions { public static string? GetRealIpAddress(this HttpRequest httpRequest) { diff --git a/src/Api/Filters/HttpResponseExceptionFilter.cs b/src/Api/Filters/HttpExceptionResponseFilter.cs similarity index 91% rename from src/Api/Filters/HttpResponseExceptionFilter.cs rename to src/Api/Filters/HttpExceptionResponseFilter.cs index 80f00b39..76ada4d5 100644 --- a/src/Api/Filters/HttpResponseExceptionFilter.cs +++ b/src/Api/Filters/HttpExceptionResponseFilter.cs @@ -5,7 +5,7 @@ namespace PoEGamblingHelper.Api.Filters; // ReSharper disable once ClassNeverInstantiated.Global -internal class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter +internal class HttpExceptionResponseFilter : IActionFilter, IOrderedFilter { public void OnActionExecuting(ActionExecutingContext context) { } diff --git a/src/Api/Program.cs b/src/Api/Program.cs index e1468959..72fbd915 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,8 +1,7 @@ using System.Globalization; -using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Api; +using PoEGamblingHelper.Api.Configuration; using PoEGamblingHelper.Api.Filters; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure; using PoEGamblingHelper.Infrastructure.Database; @@ -15,23 +14,23 @@ builder.Logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning); builder.Services.AddConfiguredRateLimiter(builder.Configuration); -builder.Services.AddControllers(options => { options.Filters.Add(); }); +builder.Services.AddControllers(options => { options.Filters.Add(); }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddInfrastructureServices(builder.Configuration); builder.Services.AddCache(builder.Configuration); -builder.Services.AddHostedService( - opt => new InitService( - opt.GetRequiredService>(), - opt.GetRequiredService(), - opt.GetRequiredService(), - TimeSpan.FromMinutes(builder.Configuration.GetValue("FetchInterval")), - builder.Configuration.GetValue("CacheTag")!, - opt.GetRequiredService(), - opt.GetRequiredService(), - opt.GetRequiredService() - ) -); +// builder.Services.AddHostedService( +// opt => new InitService( +// opt.GetRequiredService>(), +// opt.GetRequiredService(), +// opt.GetRequiredService(), +// TimeSpan.FromMinutes(builder.Configuration.GetValue("FetchInterval")), +// builder.Configuration.GetValue("CacheTag")!, +// opt.GetRequiredService(), +// opt.GetRequiredService(), +// opt.GetRequiredService() +// ) +// ); var app = builder.Build(); diff --git a/src/Application/Repositories/ILeagueRepository.cs b/src/Application/Repositories/ILeagueRepository.cs index d716b550..66b516f6 100644 --- a/src/Application/Repositories/ILeagueRepository.cs +++ b/src/Application/Repositories/ILeagueRepository.cs @@ -5,4 +5,5 @@ namespace PoEGamblingHelper.Application.Repositories; public interface ILeagueRepository { League GetByStartDateAfter(DateTime dateTime); + IAsyncEnumerable GetAllLeagues(); } \ No newline at end of file diff --git a/src/Application/Repositories/ITempleRepository.cs b/src/Application/Repositories/ITempleRepository.cs new file mode 100644 index 00000000..d5cfc187 --- /dev/null +++ b/src/Application/Repositories/ITempleRepository.cs @@ -0,0 +1,8 @@ +using PoEGamblingHelper.Domain.Entity; + +namespace PoEGamblingHelper.Application.Repositories; + +public interface ITempleRepository +{ + TempleCost GetCurrent(); +} \ No newline at end of file diff --git a/src/Application/Services/ILeagueService.cs b/src/Application/Services/ILeagueService.cs index 7bc63afd..8d09a3e9 100644 --- a/src/Application/Services/ILeagueService.cs +++ b/src/Application/Services/ILeagueService.cs @@ -5,4 +5,5 @@ namespace PoEGamblingHelper.Application.Services; public interface ILeagueService //TODO rename { League GetCurrentLeague(); + IAsyncEnumerable GetAllLeagues(); } \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs index 8522b582..f1abf898 100644 --- a/src/Application/Services/LeagueService.cs +++ b/src/Application/Services/LeagueService.cs @@ -10,11 +10,9 @@ public class LeagueService : ILeagueService public League GetCurrentLeague() { - var utcNow = DateTime.Today.ToUniversalTime(); + var utcNow = DateTime.Today.ToUniversalTime(); //TODO DateTime Today in Infrastructure return _leagueRepository.GetByStartDateAfter(utcNow); - // return leagues.Where(league => utcNow >= league.StartDate) - // .OrderByDescending(league => league.StartDate) - // .FirstOrDefault() - // ?? throw new NoLeagueDataException(); - } //TODO fix this + } + + public IAsyncEnumerable GetAllLeagues() { return _leagueRepository.GetAllLeagues(); } } \ No newline at end of file diff --git a/src/Infrastructure/Repositories/LeagueRepository.cs b/src/Infrastructure/Repositories/LeagueRepository.cs new file mode 100644 index 00000000..289aeb32 --- /dev/null +++ b/src/Infrastructure/Repositories/LeagueRepository.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.Repositories; + +public class LeagueRepository : ILeagueRepository +{ + private readonly IDbContextFactory _dbContextFactory; + + public LeagueRepository(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public League GetByStartDateAfter(DateTime dateTime) + { + using var applicationDbContext = _dbContextFactory.CreateDbContext(); + return applicationDbContext.League.Where(league => dateTime >= league.StartDate) + .OrderByDescending(league => league.StartDate) + .FirstOrDefault() + ?? throw new NoLeagueDataException(); + } + + public async IAsyncEnumerable GetAllLeagues() + { + await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync(); + await foreach (var item in applicationDbContext.League.AsAsyncEnumerable().ConfigureAwait(false)) + yield return item; + } +} \ No newline at end of file diff --git a/src/Infrastructure/Repositories/TempleRepository.cs b/src/Infrastructure/Repositories/TempleRepository.cs new file mode 100644 index 00000000..869250ea --- /dev/null +++ b/src/Infrastructure/Repositories/TempleRepository.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.Repositories; + +public class TempleRepository : ITempleRepository +{ + private readonly IDbContextFactory _dbContextFactory; + + public TempleRepository(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public TempleCost GetCurrent() + { + using var applicationDbContext = _dbContextFactory.CreateDbContext(); + return applicationDbContext.TempleCost.OrderByDescending(cost => cost.TimeStamp).FirstOrDefault() + ?? throw new NoTempleDataException(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Services/GemService.cs b/src/Infrastructure/Services/GemService.cs index f5589a1a..fe5234c5 100644 --- a/src/Infrastructure/Services/GemService.cs +++ b/src/Infrastructure/Services/GemService.cs @@ -1,18 +1,26 @@ using System.Diagnostics; using System.Text.RegularExpressions; +using Microsoft.EntityFrameworkCore; using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Application.Util; using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Infrastructure.Database; namespace PoEGamblingHelper.Infrastructure.Services; public partial class GemService : IGemService { + private readonly IDbContextFactory _dbContextFactory; private readonly IGemRepository _gemRepository; - public GemService(IGemRepository gemRepository) { _gemRepository = gemRepository; } + + public GemService(IGemRepository gemRepository, IDbContextFactory dbContextFactory) + { + _gemRepository = gemRepository; + _dbContextFactory = dbContextFactory; + } public async Task> GetAll(GemDataQuery? query, PageRequest page) { @@ -31,7 +39,7 @@ public async Task> GetAll(GemDataQuery? query, PageRequest page) private async Task> GetAll(PageRequest page) { - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync(); var allContentLength = await applicationDbContext.GemData.CountAsync(); var (skipSize, takeSize) = page.ConvertToSizes(); @@ -50,7 +58,7 @@ private GemData[] FilterGemData(GemDataQuery query) query.PricePerTryFrom ??= decimal.MinValue; query.PricePerTryTo ??= decimal.MaxValue; - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + using var applicationDbContext = _dbContextFactory.CreateDbContext(); var templeCost = applicationDbContext.TempleCost .OrderByDescending(cost => cost.TimeStamp) .FirstOrDefault() From 825be58d70b7e90e92273baccb9a5a635e0743aa Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 15:27:09 +0200 Subject: [PATCH 026/129] change Analytics Service to MiddleWare --- src/Api/Controllers/CurrencyController.cs | 11 +---------- src/Api/Controllers/GemController.cs | 9 +-------- src/Api/Controllers/LeagueController.cs | 15 ++------------- src/Api/Controllers/TempleController.cs | 17 ++--------------- src/Api/{ => Middleware}/AnalyticsMiddleware.cs | 4 +--- 5 files changed, 7 insertions(+), 49 deletions(-) rename src/Api/{ => Middleware}/AnalyticsMiddleware.cs (83%) diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 64248cd6..72679b2f 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -1,7 +1,5 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Extensions; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Repositories; @@ -9,21 +7,14 @@ namespace PoEGamblingHelper.Api.Controllers; public class CurrencyController : ApiControllerBase { - private readonly IAnalyticsService _analyticsService; private readonly CurrencyRepository _currencyRepository; - public CurrencyController(IAnalyticsService analyticsService, - CurrencyRepository currencyRepository) - { - _analyticsService = analyticsService; - _currencyRepository = currencyRepository; - } + public CurrencyController(CurrencyRepository currencyRepository) { _currencyRepository = currencyRepository; } [HttpGet] [OutputCache(PolicyName = "FetchData")] public IAsyncEnumerable GetAll() //TODO { - _analyticsService.AddView(Request.GetRealIpAddress()).RunSynchronously(); return _currencyRepository.GetAll(); } } \ No newline at end of file diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index 3a2587bb..fb05c648 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity.Gem; @@ -9,20 +8,14 @@ namespace PoEGamblingHelper.Api.Controllers; public class GemController : ApiControllerBase { - private readonly IAnalyticsService _analyticsService; private readonly IGemService _gemService; - public GemController(IGemService gemService, IAnalyticsService analyticsService) - { - _gemService = gemService; - _analyticsService = analyticsService; - } + public GemController(IGemService gemService) { _gemService = gemService; } [HttpGet] [OutputCache(PolicyName = "FetchData")] public async Task> GetAll([FromQuery] GemDataQuery? query, [FromQuery] PageRequest page) { - await _analyticsService.AddView(Request.GetRealIpAddress()); return await _gemService.GetAll(query, page); } } \ No newline at end of file diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index 3cf17d06..f02500fc 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; @@ -8,28 +7,18 @@ namespace PoEGamblingHelper.Api.Controllers; public class LeagueController : ApiControllerBase { - private readonly IAnalyticsService _analyticsService; private readonly ILeagueService _leagueService; - public LeagueController(ILeagueService leagueService, IAnalyticsService analyticsService) - { - _leagueService = leagueService; - _analyticsService = analyticsService; - } + public LeagueController(ILeagueService leagueService) { _leagueService = leagueService; } [HttpGet] public IAsyncEnumerable GetAllLeagues() //TODO { - _analyticsService.AddView(Request.GetRealIpAddress()).RunSynchronously(); return _leagueService.GetAllLeagues(); } [HttpGet] [Route("current")] [OutputCache(PolicyName = "FetchData")] - public League GetCurrentLeague() - { - _analyticsService.AddView(Request.GetRealIpAddress()); - return _leagueService.GetCurrentLeague(); - } + public League GetCurrentLeague() { return _leagueService.GetCurrentLeague(); } } \ No newline at end of file diff --git a/src/Api/Controllers/TempleController.cs b/src/Api/Controllers/TempleController.cs index c3b8a9a3..34927afd 100644 --- a/src/Api/Controllers/TempleController.cs +++ b/src/Api/Controllers/TempleController.cs @@ -1,28 +1,15 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Repositories; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; namespace PoEGamblingHelper.Api.Controllers; public class TempleController : ApiControllerBase { - private readonly IAnalyticsService _analyticsService; private readonly ITempleRepository _templeRepository; - public TempleController(IAnalyticsService analyticsService, ITempleRepository templeRepository) - { - _analyticsService = analyticsService; - _templeRepository = templeRepository; - } + public TempleController(ITempleRepository templeRepository) { _templeRepository = templeRepository; } - [HttpGet] - [OutputCache(PolicyName = "FetchData")] - public TempleCost Get() - { - _analyticsService.AddView(Request.GetRealIpAddress()); - return _templeRepository.GetCurrent(); - } + [HttpGet] [OutputCache(PolicyName = "FetchData")] public TempleCost Get() { return _templeRepository.GetCurrent(); } } \ No newline at end of file diff --git a/src/Api/AnalyticsMiddleware.cs b/src/Api/Middleware/AnalyticsMiddleware.cs similarity index 83% rename from src/Api/AnalyticsMiddleware.cs rename to src/Api/Middleware/AnalyticsMiddleware.cs index 1f105205..e5270bf6 100644 --- a/src/Api/AnalyticsMiddleware.cs +++ b/src/Api/Middleware/AnalyticsMiddleware.cs @@ -1,7 +1,7 @@ using PoEGamblingHelper.Api.Extensions; using PoEGamblingHelper.Application.Services; -namespace PoEGamblingHelper.Api; +namespace PoEGamblingHelper.Api.Middleware; public class AnalyticsMiddleware { @@ -9,10 +9,8 @@ public class AnalyticsMiddleware public AnalyticsMiddleware(RequestDelegate next) { _next = next; } - // IMessageWriter is injected into InvokeAsync public async Task InvokeAsync(HttpContext httpContext, IAnalyticsService analyticsService) { - Console.WriteLine("CALL INVOKE"); //TODO await analyticsService.AddView(httpContext.Request.GetRealIpAddress()); await _next(httpContext); } From da9bb2ee391e390042750e78e3b21099e947465e Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 15:27:26 +0200 Subject: [PATCH 027/129] cleanup --- src/Api/Program.cs | 7 +- src/Api/Properties/launchSettings.json | 2 +- .../Repositories/IViewRepository.cs | 3 +- src/Application/Services/AnalyticsService.cs | 28 +- src/Domain/Entity/Stats/Result.cs | 4 +- .../20230918130909_temp.Designer.cs | 292 +++++++++++++++++ .../Migrations/20230918130909_temp.cs | 301 ++++++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 243 ++++++++------ .../Repositories/ViewRepository.cs | 44 +++ .../ServiceCollectionExtensions.cs | 8 +- src/Infrastructure/Services/GemService.cs | 5 +- 11 files changed, 812 insertions(+), 125 deletions(-) create mode 100644 src/Infrastructure/Migrations/20230918130909_temp.Designer.cs create mode 100644 src/Infrastructure/Migrations/20230918130909_temp.cs create mode 100644 src/Infrastructure/Repositories/ViewRepository.cs diff --git a/src/Api/Program.cs b/src/Api/Program.cs index 72fbd915..a050d3df 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,7 +1,7 @@ using System.Globalization; -using PoEGamblingHelper.Api; using PoEGamblingHelper.Api.Configuration; using PoEGamblingHelper.Api.Filters; +using PoEGamblingHelper.Api.Middleware; using PoEGamblingHelper.Infrastructure; using PoEGamblingHelper.Infrastructure.Database; @@ -15,10 +15,13 @@ builder.Services.AddConfiguredRateLimiter(builder.Configuration); builder.Services.AddControllers(options => { options.Filters.Add(); }); +builder.Services.AddCache(builder.Configuration); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); + builder.Services.AddInfrastructureServices(builder.Configuration); -builder.Services.AddCache(builder.Configuration); + // builder.Services.AddHostedService( // opt => new InitService( // opt.GetRequiredService>(), diff --git a/src/Api/Properties/launchSettings.json b/src/Api/Properties/launchSettings.json index 417ee016..ff4e9e92 100644 --- a/src/Api/Properties/launchSettings.json +++ b/src/Api/Properties/launchSettings.json @@ -4,7 +4,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "applicationUrl": "https://localhost:7138;http://localhost:5190", "environmentVariables": { diff --git a/src/Application/Repositories/IViewRepository.cs b/src/Application/Repositories/IViewRepository.cs index 2d9720c9..d03f8941 100644 --- a/src/Application/Repositories/IViewRepository.cs +++ b/src/Application/Repositories/IViewRepository.cs @@ -5,5 +5,6 @@ namespace PoEGamblingHelper.Application.Repositories; public interface IViewRepository { Task AddAsync(View view); - Task LogViewsAsync(DateOnly date); + Task CountViewsAsync(DateOnly date); + Task RemoveAllAsync(DateOnly date); } \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index 3c23f74c..835809b7 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -1,5 +1,6 @@ using System.Security.Cryptography; using System.Text; +using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; @@ -7,9 +8,14 @@ namespace PoEGamblingHelper.Application.Services; public class AnalyticsService : IAnalyticsService { + private readonly ILogger _logger; private readonly IViewRepository _viewRepository; - public AnalyticsService(IViewRepository viewRepository) { _viewRepository = viewRepository; } + public AnalyticsService(IViewRepository viewRepository, ILogger logger) + { + _viewRepository = viewRepository; + _logger = logger; + } public async Task AddView(string? ipAddress) { @@ -18,24 +24,18 @@ public async Task AddView(string? ipAddress) var today = DateOnly.FromDateTime(DateTime.UtcNow); var view = new View { IpHash = ipHash, TimeStamp = today.ToDateTime(TimeOnly.MinValue) }; await _viewRepository.AddAsync(view); - // using var ctx = _applicationDbContextFactory.CreateDbContext(); - // if (await ctx.View.AsNoTracking().AnyAsync(v => v.IpHash.Equals(ipHash) && v.TimeStamp == today)) return; - - // await ctx.View.AddAsync(view); - // await ctx.SaveChangesAsync(); } public async Task LogYesterdaysViews() { var yesterday = DateOnly.FromDateTime(DateTime.UtcNow).AddDays(-1); - await _viewRepository.LogViewsAsync(yesterday); - // using var ctx = _applicationDbContextFactory.CreateDbContext(); - // var viewCount = await ctx.View.Where(v => v.TimeStamp == yesterday).CountAsync(); - // _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", - // viewCount, yesterday.Day, yesterday.Month, yesterday.Year); - // var views = await ctx.View.Where(v => v.TimeStamp <= yesterday).ToArrayAsync(); - // ctx.View.RemoveRange(views); - // await ctx.SaveChangesAsync(); + + var viewCount = await _viewRepository.CountViewsAsync(yesterday); + _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", + viewCount, yesterday.Day, yesterday.Month, yesterday.Year); + + + await _viewRepository.RemoveAllAsync(yesterday); } //TODO fix this } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/Result.cs b/src/Domain/Entity/Stats/Result.cs index 8dc5f179..42635554 100644 --- a/src/Domain/Entity/Stats/Result.cs +++ b/src/Domain/Entity/Stats/Result.cs @@ -6,8 +6,8 @@ namespace PoEGamblingHelper.Domain.Entity.Stats { public class Result : Entity { - public GemTradeData GemTradeData { get; set; } + public GemTradeData GemTradeData { get; set; } = null!; public decimal CurrencyValue { get; set; } - public CurrencyResult CurrencyResult { get; set; } + public CurrencyResult CurrencyResult { get; set; } = null!; } } \ No newline at end of file diff --git a/src/Infrastructure/Migrations/20230918130909_temp.Designer.cs b/src/Infrastructure/Migrations/20230918130909_temp.Designer.cs new file mode 100644 index 00000000..db9d3a14 --- /dev/null +++ b/src/Infrastructure/Migrations/20230918130909_temp.Designer.cs @@ -0,0 +1,292 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PoEGamblingHelper.Infrastructure.Database; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230918130909_temp")] + partial class temp + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.View", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("IpHash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("TimeStamp") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("View"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemData", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("Icon") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("GemData"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.League", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("Entity", t => + { + t.Property("Name") + .HasColumnName("League_Name"); + }); + + b.HasDiscriminator().HasValue("League"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.Result", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("CurrencyResultId") + .HasColumnType("text"); + + b.Property("CurrencyValue") + .HasColumnType("numeric"); + + b.Property("GemTradeDataId") + .HasColumnType("bigint"); + + b.HasIndex("CurrencyResultId"); + + b.HasIndex("GemTradeDataId"); + + b.HasDiscriminator().HasValue("Result"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.TempleCost", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("ChaosValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("TimeStamp") + .HasColumnType("timestamp with time zone"); + + b.ToTable("Entity", t => + { + t.Property("TimeStamp") + .HasColumnName("TempleCost_TimeStamp"); + }); + + b.HasDiscriminator().HasValue("TempleCost"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("ChaosValue") + .HasColumnType("numeric"); + + b.Property("Corrupted") + .HasColumnType("boolean"); + + b.Property("DetailsId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DivineValue") + .HasColumnType("numeric"); + + b.Property("ExaltedValue") + .HasColumnType("numeric"); + + b.Property("GemDataId") + .HasColumnType("uuid"); + + b.Property("GemLevel") + .HasColumnType("integer"); + + b.Property("GemQuality") + .HasColumnType("integer"); + + b.Property("ListingCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasIndex("GemDataId"); + + b.HasDiscriminator().HasValue("GemTradeData"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Currency", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("ChaosEquivalent") + .HasColumnType("numeric"); + + b.Property("Icon") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasDiscriminator().HasValue("Currency"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.CurrencyResult", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("ChaosEquivalent") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("Entity", t => + { + t.Property("ChaosEquivalent") + .HasColumnName("CurrencyResult_ChaosEquivalent"); + + t.Property("Name") + .HasColumnName("CurrencyResult_Name"); + }); + + b.HasDiscriminator().HasValue("CurrencyResult"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.Result", b => + { + b.HasOne("PoEGamblingHelper.Domain.Entity.Stats.CurrencyResult", "CurrencyResult") + .WithMany() + .HasForeignKey("CurrencyResultId"); + + b.HasOne("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", "GemTradeData") + .WithMany() + .HasForeignKey("GemTradeDataId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrencyResult"); + + b.Navigation("GemTradeData"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", b => + { + b.HasOne("PoEGamblingHelper.Domain.Entity.Gem.GemData", null) + .WithMany("Gems") + .HasForeignKey("GemDataId"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemData", b => + { + b.Navigation("Gems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Migrations/20230918130909_temp.cs b/src/Infrastructure/Migrations/20230918130909_temp.cs new file mode 100644 index 00000000..42315612 --- /dev/null +++ b/src/Infrastructure/Migrations/20230918130909_temp.cs @@ -0,0 +1,301 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class temp : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Currency"); + + migrationBuilder.DropTable( + name: "League"); + + migrationBuilder.DropTable( + name: "Result"); + + migrationBuilder.DropTable( + name: "TempleCost"); + + migrationBuilder.DropTable( + name: "View"); + + migrationBuilder.DropTable( + name: "CurrencyResult"); + + migrationBuilder.DropTable( + name: "GemTradeData"); + + migrationBuilder.DropTable( + name: "GemData"); + + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Discriminator = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + ChaosEquivalent = table.Column(type: "numeric", nullable: true), + Icon = table.Column(type: "text", nullable: true), + CurrencyResult_Name = table.Column(type: "text", nullable: true), + CurrencyResult_ChaosEquivalent = table.Column(type: "numeric", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Discriminator = table.Column(type: "text", nullable: false), + IpHash = table.Column(type: "bytea", nullable: true), + TimeStamp = table.Column(type: "timestamp with time zone", nullable: true), + Name = table.Column(type: "text", nullable: true), + Icon = table.Column(type: "text", nullable: true), + League_Name = table.Column(type: "text", nullable: true), + StartDate = table.Column(type: "timestamp with time zone", nullable: true), + Version = table.Column(type: "text", nullable: true), + GemTradeDataId = table.Column(type: "bigint", nullable: true), + CurrencyValue = table.Column(type: "numeric", nullable: true), + CurrencyResultId = table.Column(type: "text", nullable: true), + TempleCost_TimeStamp = table.Column(type: "timestamp with time zone", nullable: true), + ChaosValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + table.ForeignKey( + name: "FK_Entity_Entity_CurrencyResultId", + column: x => x.CurrencyResultId, + principalTable: "Entity", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Discriminator = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + GemLevel = table.Column(type: "integer", nullable: true), + GemQuality = table.Column(type: "integer", nullable: true), + Corrupted = table.Column(type: "boolean", nullable: true), + DetailsId = table.Column(type: "text", nullable: true), + ChaosValue = table.Column(type: "numeric", nullable: true), + ExaltedValue = table.Column(type: "numeric", nullable: true), + DivineValue = table.Column(type: "numeric", nullable: true), + ListingCount = table.Column(type: "integer", nullable: true), + GemDataId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + table.ForeignKey( + name: "FK_Entity_Entity_GemDataId", + column: x => x.GemDataId, + principalTable: "Entity", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Entity_CurrencyResultId", + table: "Entity", + column: "CurrencyResultId"); + + migrationBuilder.CreateIndex( + name: "IX_Entity_GemTradeDataId", + table: "Entity", + column: "GemTradeDataId"); + + migrationBuilder.CreateIndex( + name: "IX_Entity_GemDataId", + table: "Entity", + column: "GemDataId"); + + migrationBuilder.AddForeignKey( + name: "FK_Entity_Entity_GemTradeDataId", + table: "Entity", + column: "GemTradeDataId", + principalTable: "Entity", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Entity_Entity_GemTradeDataId", + table: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + + migrationBuilder.CreateTable( + name: "Currency", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ChaosEquivalent = table.Column(type: "numeric", nullable: false), + Icon = table.Column(type: "text", nullable: true), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Currency", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CurrencyResult", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ChaosEquivalent = table.Column(type: "numeric", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CurrencyResult", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GemData", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Icon = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GemData", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "League", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + StartDate = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_League", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TempleCost", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ChaosValue = table.Column(type: "text", nullable: false), + TimeStamp = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TempleCost", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "View", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IpHash = table.Column(type: "bytea", nullable: false), + TimeStamp = table.Column(type: "date", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_View", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GemTradeData", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ChaosValue = table.Column(type: "numeric", nullable: false), + Corrupted = table.Column(type: "boolean", nullable: false), + DetailsId = table.Column(type: "text", nullable: false), + DivineValue = table.Column(type: "numeric", nullable: false), + ExaltedValue = table.Column(type: "numeric", nullable: false), + GemDataId = table.Column(type: "uuid", nullable: true), + GemLevel = table.Column(type: "integer", nullable: false), + GemQuality = table.Column(type: "integer", nullable: false), + ListingCount = table.Column(type: "integer", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GemTradeData", x => x.Id); + table.ForeignKey( + name: "FK_GemTradeData_GemData_GemDataId", + column: x => x.GemDataId, + principalTable: "GemData", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Result", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CurrencyResultId = table.Column(type: "text", nullable: true), + GemTradeDataId = table.Column(type: "bigint", nullable: false), + CurrencyValue = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Result", x => x.Id); + table.ForeignKey( + name: "FK_Result_CurrencyResult_CurrencyResultId", + column: x => x.CurrencyResultId, + principalTable: "CurrencyResult", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Result_GemTradeData_GemTradeDataId", + column: x => x.GemTradeDataId, + principalTable: "GemTradeData", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GemTradeData_GemDataId", + table: "GemTradeData", + column: "GemDataId"); + + migrationBuilder.CreateIndex( + name: "IX_Result_CurrencyResultId", + table: "Result", + column: "CurrencyResultId"); + + migrationBuilder.CreateIndex( + name: "IX_Result_GemTradeDataId", + table: "Result", + column: "GemTradeDataId"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 7aa69fc2..7ccc8458 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,6 +1,5 @@ // using System; -using PoEGamblingHelper.Infrastructure.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -23,50 +22,82 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Domain.Entity.Analytics.View", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("IpHash") + b.Property("Discriminator") .IsRequired() - .HasColumnType("bytea"); - - b.Property("TimeStamp") - .HasColumnType("date"); + .HasColumnType("text"); b.HasKey("Id"); - b.ToTable("View"); + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); }); - modelBuilder.Entity("Domain.Entity.Currency", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Discriminator") + .IsRequired() .HasColumnType("text"); - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); + b.HasKey("Id"); - b.Property("Icon") + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Abstract.Entity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("text"); - b.Property("Name") + b.Property("Discriminator") .IsRequired() .HasColumnType("text"); b.HasKey("Id"); - b.ToTable("Currency"); + b.ToTable("Entity"); + + b.HasDiscriminator("Discriminator").HasValue("Entity"); + + b.UseTphMappingStrategy(); }); - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.View", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("IpHash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("TimeStamp") + .HasColumnType("timestamp with time zone"); + + b.HasDiscriminator().HasValue("View"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemData", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); b.Property("Icon") .IsRequired() @@ -76,18 +107,76 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b.HasKey("Id"); + b.HasDiscriminator().HasValue("GemData"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.League", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.ToTable("GemData"); + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("Entity", t => + { + t.Property("Name") + .HasColumnName("League_Name"); + }); + + b.HasDiscriminator().HasValue("League"); }); - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.Result", b => { - b.Property("Id") - .ValueGeneratedOnAdd() + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("CurrencyResultId") + .HasColumnType("text"); + + b.Property("CurrencyValue") + .HasColumnType("numeric"); + + b.Property("GemTradeDataId") .HasColumnType("bigint"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.HasIndex("CurrencyResultId"); + + b.HasIndex("GemTradeDataId"); + + b.HasDiscriminator().HasValue("Result"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.TempleCost", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("ChaosValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("TimeStamp") + .HasColumnType("timestamp with time zone"); + + b.ToTable("Entity", t => + { + t.Property("TimeStamp") + .HasColumnName("TempleCost_TimeStamp"); + }); + + b.HasDiscriminator().HasValue("TempleCost"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); b.Property("ChaosValue") .HasColumnType("numeric"); @@ -121,109 +210,58 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b.HasKey("Id"); - b.HasIndex("GemDataId"); - b.ToTable("GemTradeData"); + b.HasDiscriminator().HasValue("GemTradeData"); }); - modelBuilder.Entity("Domain.Entity.League", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Currency", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("League"); - }); - - modelBuilder.Entity("Domain.Entity.Stats.CurrencyResult", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); b.Property("ChaosEquivalent") .HasColumnType("numeric"); + b.Property("Icon") + .HasColumnType("text"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.HasKey("Id"); - - b.ToTable("CurrencyResult"); + b.HasDiscriminator().HasValue("Currency"); }); - modelBuilder.Entity("Domain.Entity.Stats.Result", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.CurrencyResult", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CurrencyResultId") - .HasColumnType("text"); + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); - b.Property("CurrencyValue") + b.Property("ChaosEquivalent") .HasColumnType("numeric"); - b.Property("GemTradeDataId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("CurrencyResultId"); - - b.HasIndex("GemTradeDataId"); - - b.ToTable("Result"); - }); - - modelBuilder.Entity("Domain.Entity.TempleCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ChaosValue") + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); + b.ToTable("Entity", t => + { + t.Property("ChaosEquivalent") + .HasColumnName("CurrencyResult_ChaosEquivalent"); - b.HasKey("Id"); + t.Property("Name") + .HasColumnName("CurrencyResult_Name"); + }); - b.ToTable("TempleCost"); + b.HasDiscriminator().HasValue("CurrencyResult"); }); - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Stats.Result", b => { - b.HasOne("Domain.Entity.Gem.GemData", null) - .WithMany("Gems") - .HasForeignKey("GemDataId"); - }); - - modelBuilder.Entity("Domain.Entity.Stats.Result", b => - { - b.HasOne("Domain.Entity.Stats.CurrencyResult", "CurrencyResult") + b.HasOne("PoEGamblingHelper.Domain.Entity.Stats.CurrencyResult", "CurrencyResult") .WithMany() .HasForeignKey("CurrencyResultId"); - b.HasOne("Domain.Entity.Gem.GemTradeData", "GemTradeData") + b.HasOne("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", "GemTradeData") .WithMany() .HasForeignKey("GemTradeDataId") .OnDelete(DeleteBehavior.Cascade) @@ -234,7 +272,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("GemTradeData"); }); - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemTradeData", b => + { + b.HasOne("PoEGamblingHelper.Domain.Entity.Gem.GemData", null) + .WithMany("Gems") + .HasForeignKey("GemDataId"); + }); + + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Gem.GemData", b => { b.Navigation("Gems"); }); diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs new file mode 100644 index 00000000..aa9d2668 --- /dev/null +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity.Analytics; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.Repositories; + +public class ViewRepository : IViewRepository +{ + private readonly IDbContextFactory _dbContextFactory; + + public ViewRepository(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public async Task AddAsync(View view) + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + + if (await context.View.AnyAsync(v => v.IpHash.Equals(view.IpHash) && v.TimeStamp == view.TimeStamp)) return; + + context.View.Add(view); + await context.SaveChangesAsync(); + } + + public async Task CountViewsAsync(DateOnly date) + { + var timeStamp = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.View.CountAsync(v => v.TimeStamp == timeStamp); + } + + public async Task RemoveAllAsync(DateOnly date) + { + var timeStamp = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + await using var context = await _dbContextFactory.CreateDbContextAsync(); + + var views = context.View.Where(v => v.TimeStamp <= timeStamp); + context.View.RemoveRange(views); + + await context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 365dba76..ad3f9d8c 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -1,9 +1,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure.BackgroundJobs; using PoEGamblingHelper.Infrastructure.Database; +using PoEGamblingHelper.Infrastructure.Repositories; using PoEGamblingHelper.Infrastructure.Services; namespace PoEGamblingHelper.Infrastructure; @@ -19,6 +21,9 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddBackgroundJobs(); } @@ -34,8 +39,7 @@ private static void AddDatabase(this IServiceCollection services, IConfiguration var connectionString = configuration.GetConnectionString("DBConnection") + $"Password={configuration["POSTGRES_PASSWORD"]};"; var assemblyName = typeof(ApplicationDbContext).Assembly.FullName; - services.AddEntityFrameworkNpgsql() - .AddDbContextFactory(opt => opt.UseNpgsql( + services.AddDbContextFactory(opt => opt.UseNpgsql( connectionString, builder => builder.MigrationsAssembly(assemblyName) )); diff --git a/src/Infrastructure/Services/GemService.cs b/src/Infrastructure/Services/GemService.cs index fe5234c5..c9a4ffe0 100644 --- a/src/Infrastructure/Services/GemService.cs +++ b/src/Infrastructure/Services/GemService.cs @@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore; using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.QueryParameters; -using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Application.Util; using PoEGamblingHelper.Domain.Entity.Gem; @@ -14,11 +13,9 @@ namespace PoEGamblingHelper.Infrastructure.Services; public partial class GemService : IGemService { private readonly IDbContextFactory _dbContextFactory; - private readonly IGemRepository _gemRepository; - public GemService(IGemRepository gemRepository, IDbContextFactory dbContextFactory) + public GemService(IDbContextFactory dbContextFactory) { - _gemRepository = gemRepository; _dbContextFactory = dbContextFactory; } From c3a629ec16eb39c28d26eec1d8d06cc64fe48efc Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 15:29:53 +0200 Subject: [PATCH 028/129] cleanup --- src/Api/Controllers/CurrencyController.cs | 5 +---- src/Api/Controllers/LeagueController.cs | 11 +++-------- src/Application/Services/ILeagueService.cs | 4 ++-- src/Application/Services/LeagueService.cs | 4 ++-- .../BackgroundJobs/FetchPriceDataJob.cs | 2 +- test/Application.Test/Services/InitServiceTest.cs | 6 +++--- test/Application.Test/Services/LeagueServiceTest.cs | 4 ++-- 7 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 72679b2f..59ec4327 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -13,8 +13,5 @@ public class CurrencyController : ApiControllerBase [HttpGet] [OutputCache(PolicyName = "FetchData")] - public IAsyncEnumerable GetAll() //TODO - { - return _currencyRepository.GetAll(); - } + public IAsyncEnumerable GetAll() { return _currencyRepository.GetAll(); } } \ No newline at end of file diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index f02500fc..921b5eb6 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -11,14 +11,9 @@ public class LeagueController : ApiControllerBase public LeagueController(ILeagueService leagueService) { _leagueService = leagueService; } - [HttpGet] - public IAsyncEnumerable GetAllLeagues() //TODO - { - return _leagueService.GetAllLeagues(); - } + [HttpGet] public IAsyncEnumerable GetAll() { return _leagueService.GetAll(); } - [HttpGet] - [Route("current")] + [HttpGet("current")] [OutputCache(PolicyName = "FetchData")] - public League GetCurrentLeague() { return _leagueService.GetCurrentLeague(); } + public League GetCurrent() { return _leagueService.GetCurrent(); } } \ No newline at end of file diff --git a/src/Application/Services/ILeagueService.cs b/src/Application/Services/ILeagueService.cs index 8d09a3e9..aefffdae 100644 --- a/src/Application/Services/ILeagueService.cs +++ b/src/Application/Services/ILeagueService.cs @@ -4,6 +4,6 @@ namespace PoEGamblingHelper.Application.Services; public interface ILeagueService //TODO rename { - League GetCurrentLeague(); - IAsyncEnumerable GetAllLeagues(); + League GetCurrent(); + IAsyncEnumerable GetAll(); } \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs index f1abf898..adec2282 100644 --- a/src/Application/Services/LeagueService.cs +++ b/src/Application/Services/LeagueService.cs @@ -8,11 +8,11 @@ public class LeagueService : ILeagueService private readonly ILeagueRepository _leagueRepository; public LeagueService(ILeagueRepository leagueRepository) { _leagueRepository = leagueRepository; } - public League GetCurrentLeague() + public League GetCurrent() { var utcNow = DateTime.Today.ToUniversalTime(); //TODO DateTime Today in Infrastructure return _leagueRepository.GetByStartDateAfter(utcNow); } - public IAsyncEnumerable GetAllLeagues() { return _leagueRepository.GetAllLeagues(); } + public IAsyncEnumerable GetAll() { return _leagueRepository.GetAllLeagues(); } } \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index ec866116..9ecb7a3d 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -35,7 +35,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) League league; try { - league = _leagueService.GetCurrentLeague(); + league = _leagueService.GetCurrent(); } catch (NoLeagueDataException e) { diff --git a/test/Application.Test/Services/InitServiceTest.cs b/test/Application.Test/Services/InitServiceTest.cs index 462387db..2fcadb30 100644 --- a/test/Application.Test/Services/InitServiceTest.cs +++ b/test/Application.Test/Services/InitServiceTest.cs @@ -42,7 +42,7 @@ public async Task FetchPriceDataTest() var leagueService = new Mock(); var analyticsService = new Mock(); - leagueService.Setup(s => s.GetCurrentLeague(appDbContext.Object.League)).Verifiable(); + leagueService.Setup(s => s.GetCurrent(appDbContext.Object.League)).Verifiable(); dataFetchService.Setup(s => s.FetchCurrencyData(null)).Verifiable(); dataFetchService.Setup(s => s.FetchTemplePriceData(null)).Verifiable(); dataFetchService.Setup(s => s.FetchGemPriceData(null)).Verifiable(); @@ -60,11 +60,11 @@ public async Task FetchPriceDataTest() cache.Verify(); - leagueService.Setup(s => s.GetCurrentLeague(appDbContext.Object.League)).Throws(); + leagueService.Setup(s => s.GetCurrent(appDbContext.Object.League)).Throws(); Assert.Null(await Record.ExceptionAsync(async () => await service.FetchPriceData())); leagueService.Invocations.Clear(); - leagueService.Setup(s => s.GetCurrentLeague(appDbContext.Object.League)).Returns(new League()); + leagueService.Setup(s => s.GetCurrent(appDbContext.Object.League)).Returns(new League()); dataFetchService.Setup(s => s.FetchCurrencyData(It.IsAny())) .Throws(() => new ApiDownException("poedb.tw")); diff --git a/test/Application.Test/Services/LeagueServiceTest.cs b/test/Application.Test/Services/LeagueServiceTest.cs index 8eac6cbf..bb30f8c4 100644 --- a/test/Application.Test/Services/LeagueServiceTest.cs +++ b/test/Application.Test/Services/LeagueServiceTest.cs @@ -28,7 +28,7 @@ public void GetCurrentLeagueTest() var queryable = list.AsQueryable().BuildMockDbSet(); var service = new LeagueService(); - service.GetCurrentLeague(queryable.Object).Id.Should().Be(expectedId); + service.GetCurrent(queryable.Object).Id.Should().Be(expectedId); } [Fact] @@ -37,6 +37,6 @@ public void GetCurrentLeagueEmptyTest() var queryable = new List().AsQueryable().BuildMockDbSet(); var service = new LeagueService(); - Assert.Throws(() => service.GetCurrentLeague(queryable.Object)); + Assert.Throws(() => service.GetCurrent(queryable.Object)); } } \ No newline at end of file From 3ebdbea82e253a384d70745097efebdca7ada427 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 16:08:43 +0200 Subject: [PATCH 029/129] fix analytics service --- src/Api/Controllers/CurrencyController.cs | 6 +-- src/Api/Extensions/HttpExtensions.cs | 48 +++++++++++++++++-- .../Repositories/ICurrencyRepository.cs | 8 ++++ .../Repositories/CurrencyRepository.cs | 3 +- .../Repositories/ViewRepository.cs | 2 + .../ServiceCollectionExtensions.cs | 1 + 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/Application/Repositories/ICurrencyRepository.cs diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 59ec4327..4d1c50ad 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -1,15 +1,15 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Infrastructure.Repositories; namespace PoEGamblingHelper.Api.Controllers; public class CurrencyController : ApiControllerBase { - private readonly CurrencyRepository _currencyRepository; + private readonly ICurrencyRepository _currencyRepository; - public CurrencyController(CurrencyRepository currencyRepository) { _currencyRepository = currencyRepository; } + public CurrencyController(ICurrencyRepository currencyRepository) { _currencyRepository = currencyRepository; } [HttpGet] [OutputCache(PolicyName = "FetchData")] diff --git a/src/Api/Extensions/HttpExtensions.cs b/src/Api/Extensions/HttpExtensions.cs index 6332826f..300e36ef 100644 --- a/src/Api/Extensions/HttpExtensions.cs +++ b/src/Api/Extensions/HttpExtensions.cs @@ -1,12 +1,54 @@ -namespace PoEGamblingHelper.Api.Extensions; +using System.Runtime.InteropServices; + +namespace PoEGamblingHelper.Api.Extensions; public static class HttpExtensions { public static string? GetRealIpAddress(this HttpRequest httpRequest) { - var forwardedChain = httpRequest.HttpContext.Request.Headers["X-Forwarded-For"].First(); - if (string.IsNullOrEmpty(forwardedChain)) return httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString(); + var forwardedChain = httpRequest.HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(forwardedChain)) + { + Console.WriteLine("___ MAC ADDERSS ___"); + Console.WriteLine(GetClientMac(httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString())); + return httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString(); + } + var clientIp = forwardedChain.Split(',').First(); + Console.WriteLine(GetClientMac(clientIp)); return clientIp; } + + [DllImport("Iphlpapi.dll")] private static extern int SendARP(int dest, int host, ref long mac, ref int length); + + [DllImport("Ws2_32.dll")] private static extern int inet_addr(string ip); + + private static string GetClientMac(string strClientIp) + { + var mac_dest = ""; + try + { + var ldest = inet_addr(strClientIp); + var lhost = inet_addr(""); + var macinfo = new long(); + var len = 6; + var res = SendARP(ldest, 0, ref macinfo, ref len); + var mac_src = macinfo.ToString("X"); + + while (mac_src.Length < 12) mac_src = mac_src.Insert(0, "0"); + + for (var i = 0; i < 11; i++) + if (0 == i % 2) + { + if (i == 10) mac_dest = mac_dest.Insert(0, mac_src.Substring(i, 2)); + else mac_dest = "-" + mac_dest.Insert(0, mac_src.Substring(i, 2)); + } + } + catch (Exception err) + { + throw new Exception("Lỗi " + err.Message); + } + + return mac_dest; + } } \ No newline at end of file diff --git a/src/Application/Repositories/ICurrencyRepository.cs b/src/Application/Repositories/ICurrencyRepository.cs new file mode 100644 index 00000000..14ebacc4 --- /dev/null +++ b/src/Application/Repositories/ICurrencyRepository.cs @@ -0,0 +1,8 @@ +using PoEGamblingHelper.Domain.Entity; + +namespace PoEGamblingHelper.Application.Repositories; + +public interface ICurrencyRepository +{ + IAsyncEnumerable GetAll(); +} \ No newline at end of file diff --git a/src/Infrastructure/Repositories/CurrencyRepository.cs b/src/Infrastructure/Repositories/CurrencyRepository.cs index 994a4148..f566aa2e 100644 --- a/src/Infrastructure/Repositories/CurrencyRepository.cs +++ b/src/Infrastructure/Repositories/CurrencyRepository.cs @@ -1,10 +1,11 @@ using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Database; namespace PoEGamblingHelper.Infrastructure.Repositories; -public class CurrencyRepository +public class CurrencyRepository : ICurrencyRepository { private readonly IDbContextFactory _dbContextFactory; diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs index aa9d2668..29dfec76 100644 --- a/src/Infrastructure/Repositories/ViewRepository.cs +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -16,6 +16,8 @@ public ViewRepository(IDbContextFactory dbContextFactory) public async Task AddAsync(View view) { + view.TimeStamp = new DateTime(view.TimeStamp.Ticks, DateTimeKind.Utc); + await using var context = await _dbContextFactory.CreateDbContextAsync(); if (await context.View.AnyAsync(v => v.IpHash.Equals(view.IpHash) && v.TimeStamp == view.TimeStamp)) return; diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index ad3f9d8c..ab0c7593 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -20,6 +20,7 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); From e0feb543c14300bd2444ea07c02d868d7d315938 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 17:48:54 +0200 Subject: [PATCH 030/129] sad --- src/Api/Extensions/HttpExtensions.cs | 45 ++-------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/src/Api/Extensions/HttpExtensions.cs b/src/Api/Extensions/HttpExtensions.cs index 300e36ef..e68b2ba7 100644 --- a/src/Api/Extensions/HttpExtensions.cs +++ b/src/Api/Extensions/HttpExtensions.cs @@ -1,54 +1,13 @@ -using System.Runtime.InteropServices; - -namespace PoEGamblingHelper.Api.Extensions; +namespace PoEGamblingHelper.Api.Extensions; public static class HttpExtensions { public static string? GetRealIpAddress(this HttpRequest httpRequest) { var forwardedChain = httpRequest.HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); - if (string.IsNullOrEmpty(forwardedChain)) - { - Console.WriteLine("___ MAC ADDERSS ___"); - Console.WriteLine(GetClientMac(httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString())); - return httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString(); - } + if (string.IsNullOrEmpty(forwardedChain)) return httpRequest.HttpContext.Connection.RemoteIpAddress?.ToString(); var clientIp = forwardedChain.Split(',').First(); - Console.WriteLine(GetClientMac(clientIp)); return clientIp; } - - [DllImport("Iphlpapi.dll")] private static extern int SendARP(int dest, int host, ref long mac, ref int length); - - [DllImport("Ws2_32.dll")] private static extern int inet_addr(string ip); - - private static string GetClientMac(string strClientIp) - { - var mac_dest = ""; - try - { - var ldest = inet_addr(strClientIp); - var lhost = inet_addr(""); - var macinfo = new long(); - var len = 6; - var res = SendARP(ldest, 0, ref macinfo, ref len); - var mac_src = macinfo.ToString("X"); - - while (mac_src.Length < 12) mac_src = mac_src.Insert(0, "0"); - - for (var i = 0; i < 11; i++) - if (0 == i % 2) - { - if (i == 10) mac_dest = mac_dest.Insert(0, mac_src.Substring(i, 2)); - else mac_dest = "-" + mac_dest.Insert(0, mac_src.Substring(i, 2)); - } - } - catch (Exception err) - { - throw new Exception("Lỗi " + err.Message); - } - - return mac_dest; - } } \ No newline at end of file From bf323e37bdfc80737ab70c41986236e4eae4ba89 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 17:51:27 +0200 Subject: [PATCH 031/129] cleanup --- src/Application/Extensions/DateTimeExtensions.cs | 9 +++++++++ src/Application/Services/AnalyticsService.cs | 3 ++- src/Infrastructure/Repositories/ViewRepository.cs | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 src/Application/Extensions/DateTimeExtensions.cs diff --git a/src/Application/Extensions/DateTimeExtensions.cs b/src/Application/Extensions/DateTimeExtensions.cs new file mode 100644 index 00000000..dfaa5614 --- /dev/null +++ b/src/Application/Extensions/DateTimeExtensions.cs @@ -0,0 +1,9 @@ +namespace PoEGamblingHelper.Application.Extensions; + +public static class DateTimeExtensions +{ + public static DateTime ToUtcDateTime(this DateOnly dateOnly) + { + return dateOnly.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + } +} \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index 835809b7..cc0833c5 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -1,6 +1,7 @@ using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; @@ -22,7 +23,7 @@ public async Task AddView(string? ipAddress) if (ipAddress is null) return; var ipHash = SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); var today = DateOnly.FromDateTime(DateTime.UtcNow); - var view = new View { IpHash = ipHash, TimeStamp = today.ToDateTime(TimeOnly.MinValue) }; + var view = new View { IpHash = ipHash, TimeStamp = today.ToUtcDateTime() }; await _viewRepository.AddAsync(view); } diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs index 29dfec76..f5a73f7f 100644 --- a/src/Infrastructure/Repositories/ViewRepository.cs +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; using PoEGamblingHelper.Infrastructure.Database; @@ -28,14 +29,14 @@ public async Task AddAsync(View view) public async Task CountViewsAsync(DateOnly date) { - var timeStamp = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + var timeStamp = date.ToUtcDateTime(); await using var context = await _dbContextFactory.CreateDbContextAsync(); return await context.View.CountAsync(v => v.TimeStamp == timeStamp); } public async Task RemoveAllAsync(DateOnly date) { - var timeStamp = date.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + var timeStamp = date.ToUtcDateTime(); await using var context = await _dbContextFactory.CreateDbContextAsync(); var views = context.View.Where(v => v.TimeStamp <= timeStamp); From 5fa86ecbf1bce2348effe0ecdf16a4f2fcf113ea Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:08:19 +0200 Subject: [PATCH 032/129] fix analytics --- .../Repositories/IAnalyticsDayRepository.cs | 6 ++++ src/Application/Services/AnalyticsService.cs | 11 +++++--- src/Domain/Domain.csproj | 2 +- src/Domain/Entity/Analytics/AnalyticsDay.cs | 10 +++++++ src/Domain/Entity/Analytics/View.cs | 11 ++++---- src/Domain/Entity/Currency.cs | 13 ++++----- src/Domain/Entity/Gem/AlternateQuality.cs | 13 ++++----- src/Domain/Entity/Gem/Case.cs | 13 ++++----- src/Domain/Entity/Gem/GemData.cs | 13 ++++----- src/Domain/Entity/Gem/GemTradeData.cs | 25 ++++++++--------- src/Domain/Entity/League.cs | 19 ++++++------- src/Domain/Entity/Stats/CurrencyResult.cs | 11 ++++---- src/Domain/Entity/Stats/Result.cs | 13 ++++----- src/Domain/Entity/TempleCost.cs | 13 ++++----- .../Database/ApplicationDbContext.cs | 1 + .../Repositories/AnalyticsDayRepository.cs | 28 +++++++++++++++++++ .../Repositories/ViewRepository.cs | 9 ++---- 17 files changed, 122 insertions(+), 89 deletions(-) create mode 100644 src/Application/Repositories/IAnalyticsDayRepository.cs create mode 100644 src/Domain/Entity/Analytics/AnalyticsDay.cs create mode 100644 src/Infrastructure/Repositories/AnalyticsDayRepository.cs diff --git a/src/Application/Repositories/IAnalyticsDayRepository.cs b/src/Application/Repositories/IAnalyticsDayRepository.cs new file mode 100644 index 00000000..1b7071f0 --- /dev/null +++ b/src/Application/Repositories/IAnalyticsDayRepository.cs @@ -0,0 +1,6 @@ +namespace PoEGamblingHelper.Application.Repositories; + +public interface IAnalyticsDayRepository +{ + Task AddAsync(int viewCount); +} \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index cc0833c5..a1fb8d92 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -1,7 +1,6 @@ using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Logging; -using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; @@ -9,13 +8,17 @@ namespace PoEGamblingHelper.Application.Services; public class AnalyticsService : IAnalyticsService { + private readonly IAnalyticsDayRepository _analyticsDayRepository; private readonly ILogger _logger; private readonly IViewRepository _viewRepository; - public AnalyticsService(IViewRepository viewRepository, ILogger logger) + public AnalyticsService(IViewRepository viewRepository, + ILogger logger, + IAnalyticsDayRepository analyticsDayRepository) { _viewRepository = viewRepository; _logger = logger; + _analyticsDayRepository = analyticsDayRepository; } public async Task AddView(string? ipAddress) @@ -23,7 +26,7 @@ public async Task AddView(string? ipAddress) if (ipAddress is null) return; var ipHash = SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); var today = DateOnly.FromDateTime(DateTime.UtcNow); - var view = new View { IpHash = ipHash, TimeStamp = today.ToUtcDateTime() }; + var view = new View { IpHash = ipHash, TimeStamp = today }; await _viewRepository.AddAsync(view); } @@ -36,7 +39,7 @@ public async Task LogYesterdaysViews() viewCount, yesterday.Day, yesterday.Month, yesterday.Year); + await _analyticsDayRepository.AddAsync(viewCount); await _viewRepository.RemoveAllAsync(yesterday); } - //TODO fix this } \ No newline at end of file diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index 3718f955..62f43190 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -1,6 +1,6 @@ - netstandard2.1 + net7.0 disable diff --git a/src/Domain/Entity/Analytics/AnalyticsDay.cs b/src/Domain/Entity/Analytics/AnalyticsDay.cs new file mode 100644 index 00000000..3bad25d0 --- /dev/null +++ b/src/Domain/Entity/Analytics/AnalyticsDay.cs @@ -0,0 +1,10 @@ +using System; +using PoEGamblingHelper.Domain.Entity.Abstract; + +namespace PoEGamblingHelper.Domain.Entity.Analytics; + +public class AnalyticsDay : Entity +{ + public DateOnly TimeStamp { get; set; } + public long Views { get; set; } +} \ No newline at end of file diff --git a/src/Domain/Entity/Analytics/View.cs b/src/Domain/Entity/Analytics/View.cs index 67181e7a..24254ed7 100644 --- a/src/Domain/Entity/Analytics/View.cs +++ b/src/Domain/Entity/Analytics/View.cs @@ -1,11 +1,10 @@ using System; using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity.Analytics +namespace PoEGamblingHelper.Domain.Entity.Analytics; + +public class View : Entity { - public class View : Entity - { - public byte[] IpHash { get; set; } = null!; - public DateTime TimeStamp { get; set; } - } + public byte[] IpHash { get; set; } = null!; + public DateOnly TimeStamp { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entity/Currency.cs b/src/Domain/Entity/Currency.cs index 9b812c6b..d382b6db 100644 --- a/src/Domain/Entity/Currency.cs +++ b/src/Domain/Entity/Currency.cs @@ -1,11 +1,10 @@ using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity +namespace PoEGamblingHelper.Domain.Entity; + +public class Currency : Entity { - public class Currency : Entity - { - public string Name { get; set; } = string.Empty; - public decimal ChaosEquivalent { get; set; } - public string? Icon { get; set; } - } + public string Name { get; set; } = string.Empty; + public decimal ChaosEquivalent { get; set; } + public string? Icon { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/AlternateQuality.cs b/src/Domain/Entity/Gem/AlternateQuality.cs index 14a0e073..7313f5d3 100644 --- a/src/Domain/Entity/Gem/AlternateQuality.cs +++ b/src/Domain/Entity/Gem/AlternateQuality.cs @@ -1,11 +1,10 @@ // ReSharper disable UnusedMember.Global -namespace PoEGamblingHelper.Domain.Entity.Gem +namespace PoEGamblingHelper.Domain.Entity.Gem; + +public enum AlternateQuality { - public enum AlternateQuality - { - Anomalous, - Divergent, - Phantasmal - } + Anomalous, + Divergent, + Phantasmal } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/Case.cs b/src/Domain/Entity/Gem/Case.cs index d25d0115..dd184ac0 100644 --- a/src/Domain/Entity/Gem/Case.cs +++ b/src/Domain/Entity/Gem/Case.cs @@ -1,9 +1,8 @@ -namespace PoEGamblingHelper.Domain.Entity.Gem +namespace PoEGamblingHelper.Domain.Entity.Gem; + +public enum ResultCase { - public enum ResultCase - { - Worst, - Middle, - Best - } + Worst, + Middle, + Best } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/GemData.cs b/src/Domain/Entity/Gem/GemData.cs index a8a0ffb0..e833c95e 100644 --- a/src/Domain/Entity/Gem/GemData.cs +++ b/src/Domain/Entity/Gem/GemData.cs @@ -5,12 +5,11 @@ [assembly: InternalsVisibleTo("Domain.Test")] -namespace PoEGamblingHelper.Domain.Entity.Gem +namespace PoEGamblingHelper.Domain.Entity.Gem; + +public class GemData : Entity { - public class GemData : Entity - { - public string Name { get; set; } = string.Empty; - public string Icon { get; set; } = string.Empty; - public IList Gems { get; set; } = new List(); - } + public string Name { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public IList Gems { get; set; } = new List(); } \ No newline at end of file diff --git a/src/Domain/Entity/Gem/GemTradeData.cs b/src/Domain/Entity/Gem/GemTradeData.cs index 43bbc66b..5f105087 100644 --- a/src/Domain/Entity/Gem/GemTradeData.cs +++ b/src/Domain/Entity/Gem/GemTradeData.cs @@ -1,17 +1,16 @@ using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity.Gem +namespace PoEGamblingHelper.Domain.Entity.Gem; + +public class GemTradeData : Entity { - public class GemTradeData : Entity - { - public string Name { get; set; } = string.Empty; - public int GemLevel { get; set; } - public int GemQuality { get; set; } - public bool Corrupted { get; set; } - public string DetailsId { get; set; } = null!; - public decimal ChaosValue { get; set; } - public decimal ExaltedValue { get; set; } - public decimal DivineValue { get; set; } - public int ListingCount { get; set; } - } + public string Name { get; set; } = null!; + public int GemLevel { get; set; } + public int GemQuality { get; set; } + public bool Corrupted { get; set; } + public string DetailsId { get; set; } = null!; + public decimal ChaosValue { get; set; } + public decimal ExaltedValue { get; set; } + public decimal DivineValue { get; set; } + public int ListingCount { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entity/League.cs b/src/Domain/Entity/League.cs index 4bb1c2c6..fe402aeb 100644 --- a/src/Domain/Entity/League.cs +++ b/src/Domain/Entity/League.cs @@ -1,17 +1,16 @@ using System; using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity +namespace PoEGamblingHelper.Domain.Entity; + +public class League : Entity { - public class League : Entity - { - public string Name { get; set; } = null!; - public DateTime StartDate { get; set; } - public string Version { get; set; } = null!; + public string Name { get; set; } = null!; + public DateTime StartDate { get; set; } + public string Version { get; set; } = null!; - public override string ToString() - { - return $"[id={Id}, Version={Version}, Name={Name}, StartDate={StartDate.ToLongDateString()}]"; - } + public override string ToString() + { + return $"[id={Id}, Version={Version}, Name={Name}, StartDate={StartDate.ToLongDateString()}]"; } } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/CurrencyResult.cs b/src/Domain/Entity/Stats/CurrencyResult.cs index 74ca3b9b..d0b87dab 100644 --- a/src/Domain/Entity/Stats/CurrencyResult.cs +++ b/src/Domain/Entity/Stats/CurrencyResult.cs @@ -1,10 +1,9 @@ using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity.Stats +namespace PoEGamblingHelper.Domain.Entity.Stats; + +public class CurrencyResult : Entity { - public class CurrencyResult : Entity - { - public string Name { get; set; } = string.Empty; - public decimal ChaosEquivalent { get; set; } - } + public string Name { get; set; } = string.Empty; + public decimal ChaosEquivalent { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entity/Stats/Result.cs b/src/Domain/Entity/Stats/Result.cs index 42635554..0eba4242 100644 --- a/src/Domain/Entity/Stats/Result.cs +++ b/src/Domain/Entity/Stats/Result.cs @@ -2,12 +2,11 @@ using PoEGamblingHelper.Domain.Entity.Abstract; using PoEGamblingHelper.Domain.Entity.Gem; -namespace PoEGamblingHelper.Domain.Entity.Stats +namespace PoEGamblingHelper.Domain.Entity.Stats; + +public class Result : Entity { - public class Result : Entity - { - public GemTradeData GemTradeData { get; set; } = null!; - public decimal CurrencyValue { get; set; } - public CurrencyResult CurrencyResult { get; set; } = null!; - } + public GemTradeData GemTradeData { get; set; } = null!; + public decimal CurrencyValue { get; set; } + public CurrencyResult CurrencyResult { get; set; } = null!; } \ No newline at end of file diff --git a/src/Domain/Entity/TempleCost.cs b/src/Domain/Entity/TempleCost.cs index 0c59e57c..15bf9253 100644 --- a/src/Domain/Entity/TempleCost.cs +++ b/src/Domain/Entity/TempleCost.cs @@ -2,12 +2,11 @@ using System.Linq; using PoEGamblingHelper.Domain.Entity.Abstract; -namespace PoEGamblingHelper.Domain.Entity +namespace PoEGamblingHelper.Domain.Entity; + +public class TempleCost : Entity { - public class TempleCost : Entity - { - public DateTime TimeStamp { get; set; } = DateTime.Now.ToUniversalTime(); - public decimal[] ChaosValue { get; set; } = Array.Empty(); - public decimal AverageChaosValue() { return ChaosValue.Average(); } - } + public DateTime TimeStamp { get; set; } = DateTime.UtcNow; + public decimal[] ChaosValue { get; set; } = Array.Empty(); + public decimal AverageChaosValue() { return ChaosValue.Average(); } } \ No newline at end of file diff --git a/src/Infrastructure/Database/ApplicationDbContext.cs b/src/Infrastructure/Database/ApplicationDbContext.cs index 548c9cf2..98b4c815 100644 --- a/src/Infrastructure/Database/ApplicationDbContext.cs +++ b/src/Infrastructure/Database/ApplicationDbContext.cs @@ -16,6 +16,7 @@ public ApplicationDbContext(DbContextOptions options) : ba public virtual DbSet GemData => Set(); public virtual DbSet GemTradeData => Set(); public virtual DbSet Result => Set(); + public virtual DbSet AnalyticsDay => Set(); public virtual DbSet View => Set(); public Task SaveChangesAsync() { return base.SaveChangesAsync(); } diff --git a/src/Infrastructure/Repositories/AnalyticsDayRepository.cs b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs new file mode 100644 index 00000000..dd8f5add --- /dev/null +++ b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Domain.Entity.Analytics; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.Repositories; + +public class AnalyticsDayRepository : IAnalyticsDayRepository +{ + private readonly IDbContextFactory _dbContextFactory; + + public AnalyticsDayRepository(IDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public async Task AddAsync(int viewCount) + { + var analyticsDay = new AnalyticsDay() + { + Views = viewCount, + TimeStamp = DateOnly.FromDateTime(DateTime.UtcNow) //TODO remove UtcNow + }; + await using var context = await _dbContextFactory.CreateDbContextAsync(); + context.AnalyticsDay.Add(analyticsDay); + await context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs index f5a73f7f..597aa3cb 100644 --- a/src/Infrastructure/Repositories/ViewRepository.cs +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; using PoEGamblingHelper.Infrastructure.Database; @@ -17,8 +16,6 @@ public ViewRepository(IDbContextFactory dbContextFactory) public async Task AddAsync(View view) { - view.TimeStamp = new DateTime(view.TimeStamp.Ticks, DateTimeKind.Utc); - await using var context = await _dbContextFactory.CreateDbContextAsync(); if (await context.View.AnyAsync(v => v.IpHash.Equals(view.IpHash) && v.TimeStamp == view.TimeStamp)) return; @@ -29,17 +26,15 @@ public async Task AddAsync(View view) public async Task CountViewsAsync(DateOnly date) { - var timeStamp = date.ToUtcDateTime(); await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.View.CountAsync(v => v.TimeStamp == timeStamp); + return await context.View.CountAsync(v => v.TimeStamp == date); } public async Task RemoveAllAsync(DateOnly date) { - var timeStamp = date.ToUtcDateTime(); await using var context = await _dbContextFactory.CreateDbContextAsync(); - var views = context.View.Where(v => v.TimeStamp <= timeStamp); + var views = context.View.Where(v => v.TimeStamp <= date); context.View.RemoveRange(views); await context.SaveChangesAsync(); From a16a8dc3cb5299b59c4d153c31fdc50a37fbef42 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:17:20 +0200 Subject: [PATCH 033/129] cleanup --- .../Extensions/GemDataExtensions.cs | 5 +++-- .../QueryParameters/PageRequest.cs | 2 ++ src/Application/Util/ExtensionMethods.cs | 14 ------------ .../Services/DataFetchService.cs | 22 +++++++++++-------- src/Infrastructure/Services/GemService.cs | 1 - src/Web/Services/Interfaces/HttpService.cs | 1 + 6 files changed, 19 insertions(+), 26 deletions(-) delete mode 100644 src/Application/Util/ExtensionMethods.cs diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index 6bcf9695..cbe0599f 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -1,10 +1,11 @@ -using PoEGamblingHelper.Application.Util; -using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Application.Extensions; public static class GemDataExtensions { + public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } + public static decimal AvgProfitPerTry(this GemData gemData, decimal? rawCost = null, decimal? worstCaseValue = null, diff --git a/src/Application/QueryParameters/PageRequest.cs b/src/Application/QueryParameters/PageRequest.cs index 36066cbe..cb445385 100644 --- a/src/Application/QueryParameters/PageRequest.cs +++ b/src/Application/QueryParameters/PageRequest.cs @@ -10,4 +10,6 @@ public int PageSize get => _pageSize; set => _pageSize = value <= 0 ? 0 : value; } + + public (int skipSize, int takeSize) ConvertToSizes() { return (PageSize * PageNumber, PageSize); } } \ No newline at end of file diff --git a/src/Application/Util/ExtensionMethods.cs b/src/Application/Util/ExtensionMethods.cs deleted file mode 100644 index b7b330b4..00000000 --- a/src/Application/Util/ExtensionMethods.cs +++ /dev/null @@ -1,14 +0,0 @@ -using PoEGamblingHelper.Application.QueryParameters; -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Application.Util; - -public static class ExtensionMethods -{ - public static (int skipSize, int takeSize) ConvertToSizes(this PageRequest pageRequest) - { - return (pageRequest.PageSize * pageRequest.PageNumber, pageRequest.PageSize); - } - - public static int LevelModifier(this ResultCase resultCase) { return (int)resultCase - 1; } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/DataFetchService.cs b/src/Infrastructure/Services/DataFetchService.cs index 015555e3..7e16eb0b 100644 --- a/src/Infrastructure/Services/DataFetchService.cs +++ b/src/Infrastructure/Services/DataFetchService.cs @@ -15,22 +15,22 @@ namespace PoEGamblingHelper.Infrastructure.Services; -public partial class DataFetchService : IDataFetchService, IDisposable +public partial class DataFetchService : IDataFetchService { private readonly IDbContextFactory _applicationDbContextFactory; private readonly HtmlWeb _htmlLoader = new(); - private readonly HttpClient _httpClient = new(); + private readonly IHttpClientFactory _httpClientFactory; private readonly MediaTypeHeaderValue _jsonMediaTypeHeader = MediaTypeHeaderValue.Parse("application/json"); private readonly ILogger _logger; private readonly string _templeQuery; public DataFetchService(ILogger logger, - IDbContextFactory applicationDbContextFactory) + IDbContextFactory applicationDbContextFactory, + IHttpClientFactory httpClientFactory) { _logger = logger; _applicationDbContextFactory = applicationDbContextFactory; - _httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); - _httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + _httpClientFactory = httpClientFactory; _templeQuery = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/TempleQuery.json"); } @@ -243,8 +243,6 @@ await applicationDbContext.GemData.AddRangeAsync( } } - public void Dispose() { _httpClient.Dispose(); } - #region Helper Methods private static (int versionColumn, int nameColumn, int releaseColumn) GetIndexesFromTitleRow(HtmlNode titleRow) @@ -317,7 +315,10 @@ private async Task GetAsync(string url) { try { - return await _httpClient.GetAsync(url); + var httpClient = _httpClientFactory.CreateClient(); + httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); + httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + return await httpClient.GetAsync(url); } catch (HttpRequestException e) { @@ -329,7 +330,10 @@ private async Task SendAsync(HttpRequestMessage request) { try { - return await _httpClient.SendAsync(request); + var httpClient = _httpClientFactory.CreateClient(); + httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); + httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + return await httpClient.SendAsync(request); } catch (HttpRequestException e) { diff --git a/src/Infrastructure/Services/GemService.cs b/src/Infrastructure/Services/GemService.cs index c9a4ffe0..ea8497ea 100644 --- a/src/Infrastructure/Services/GemService.cs +++ b/src/Infrastructure/Services/GemService.cs @@ -4,7 +4,6 @@ using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Services; -using PoEGamblingHelper.Application.Util; using PoEGamblingHelper.Domain.Entity.Gem; using PoEGamblingHelper.Infrastructure.Database; diff --git a/src/Web/Services/Interfaces/HttpService.cs b/src/Web/Services/Interfaces/HttpService.cs index c0643682..d21d63cc 100644 --- a/src/Web/Services/Interfaces/HttpService.cs +++ b/src/Web/Services/Interfaces/HttpService.cs @@ -1,5 +1,6 @@ using System.Net.Http.Json; using Blazored.Toast.Services; +using PoEGamblingHelper.Application.Exception.Body; using PoEGamblingHelper.Web.Util; namespace PoEGamblingHelper.Web.Services.Interfaces; From 8b7ac7af19090823b26a8bd6f2af43505bab158a Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:19:30 +0200 Subject: [PATCH 034/129] cleanup --- src/Api/Api.csproj | 8 ++++---- src/Domain/Entity/Abstract/Entity.cs | 9 ++++----- .../Repositories/AnalyticsDayRepository.cs | 2 +- src/Web/JavaScript/src/scroll-to-bottom.ts | 1 + src/Web/Shared/Components/GemStats.razor | 2 +- src/Web/Shared/Components/InfiniteScrolling.razor | 2 +- src/Web/_Imports.razor | 6 +++--- test/Application.Test/Util/ExtensionMethodsTest.cs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index fb28f65d..21fbbf68 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -8,8 +8,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -17,8 +17,8 @@ - - + + diff --git a/src/Domain/Entity/Abstract/Entity.cs b/src/Domain/Entity/Abstract/Entity.cs index 443c39ba..36718faf 100644 --- a/src/Domain/Entity/Abstract/Entity.cs +++ b/src/Domain/Entity/Abstract/Entity.cs @@ -1,7 +1,6 @@ -namespace PoEGamblingHelper.Domain.Entity.Abstract +namespace PoEGamblingHelper.Domain.Entity.Abstract; + +public abstract class Entity { - public abstract class Entity - { - public T Id { get; set; } = default!; - } + public T Id { get; set; } = default!; } \ No newline at end of file diff --git a/src/Infrastructure/Repositories/AnalyticsDayRepository.cs b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs index dd8f5add..5c2eba85 100644 --- a/src/Infrastructure/Repositories/AnalyticsDayRepository.cs +++ b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs @@ -16,7 +16,7 @@ public AnalyticsDayRepository(IDbContextFactory dbContextF public async Task AddAsync(int viewCount) { - var analyticsDay = new AnalyticsDay() + var analyticsDay = new AnalyticsDay { Views = viewCount, TimeStamp = DateOnly.FromDateTime(DateTime.UtcNow) //TODO remove UtcNow diff --git a/src/Web/JavaScript/src/scroll-to-bottom.ts b/src/Web/JavaScript/src/scroll-to-bottom.ts index b82dc988..2e935c43 100644 --- a/src/Web/JavaScript/src/scroll-to-bottom.ts +++ b/src/Web/JavaScript/src/scroll-to-bottom.ts @@ -20,6 +20,7 @@ window.onscroll = () => { window.RegisterScrollInfoService = (scrollInfoService: any) => window.scrollInfoService = scrollInfoService; window.UnRegisterScrollInfoService = UnRegisterScrollInfoService; + export function UnRegisterScrollInfoService() { window.scrollInfoService = undefined; } diff --git a/src/Web/Shared/Components/GemStats.razor b/src/Web/Shared/Components/GemStats.razor index 6cd96d3a..5c542b4c 100644 --- a/src/Web/Shared/Components/GemStats.razor +++ b/src/Web/Shared/Components/GemStats.razor @@ -1,4 +1,4 @@ -@using Domain.Entity.Gem +@using PoEGamblingHelper.Domain.Entity.Gem
diff --git a/src/Web/Shared/Components/InfiniteScrolling.razor b/src/Web/Shared/Components/InfiniteScrolling.razor index a37dfc69..94f1eb3b 100644 --- a/src/Web/Shared/Components/InfiniteScrolling.razor +++ b/src/Web/Shared/Components/InfiniteScrolling.razor @@ -1,4 +1,4 @@ -@using Web.Services.Interfaces +@using PoEGamblingHelper.Web.Services.Interfaces @implements IDisposable @code { diff --git a/src/Web/_Imports.razor b/src/Web/_Imports.razor index f7df935f..202bf491 100644 --- a/src/Web/_Imports.razor +++ b/src/Web/_Imports.razor @@ -6,8 +6,8 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop -@using Web.Shared -@using Web.Util -@using Web.Shared.Components +@using PoEGamblingHelper.Web.Shared +@using PoEGamblingHelper.Web.Util +@using PoEGamblingHelper.Web.Shared.Components @using Blazored.Toast @using Blazored.Toast.Services \ No newline at end of file diff --git a/test/Application.Test/Util/ExtensionMethodsTest.cs b/test/Application.Test/Util/ExtensionMethodsTest.cs index 468ed1c5..73240b9c 100644 --- a/test/Application.Test/Util/ExtensionMethodsTest.cs +++ b/test/Application.Test/Util/ExtensionMethodsTest.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using PoEGamblingHelper.Application.Util; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Application.Test.Util; From 53ca90a2e1f91fc4e71611c8eaf18620c3164280 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:30:02 +0200 Subject: [PATCH 035/129] cleanup --- .../Extensions/DateTimeExtensions.cs | 9 --------- .../Repositories/IViewRepository.cs | 6 ++---- src/Application/Services/AnalyticsService.cs | 18 +++++++----------- src/Application/Services/IAnalyticsService.cs | 2 +- src/Application/Services/IDateTimeService.cs | 7 +++++++ src/Application/Services/IHashingService.cs | 6 ++++++ .../Repositories/ViewRepository.cs | 17 +++++++++++++++-- src/Infrastructure/Services/DateTimeService.cs | 10 ++++++++++ src/Infrastructure/Services/HashingService.cs | 10 ++++++++++ .../Services/LeagueServiceTest.cs | 2 +- 10 files changed, 59 insertions(+), 28 deletions(-) delete mode 100644 src/Application/Extensions/DateTimeExtensions.cs create mode 100644 src/Application/Services/IDateTimeService.cs create mode 100644 src/Application/Services/IHashingService.cs create mode 100644 src/Infrastructure/Services/DateTimeService.cs create mode 100644 src/Infrastructure/Services/HashingService.cs diff --git a/src/Application/Extensions/DateTimeExtensions.cs b/src/Application/Extensions/DateTimeExtensions.cs deleted file mode 100644 index dfaa5614..00000000 --- a/src/Application/Extensions/DateTimeExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace PoEGamblingHelper.Application.Extensions; - -public static class DateTimeExtensions -{ - public static DateTime ToUtcDateTime(this DateOnly dateOnly) - { - return dateOnly.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); - } -} \ No newline at end of file diff --git a/src/Application/Repositories/IViewRepository.cs b/src/Application/Repositories/IViewRepository.cs index d03f8941..f666754c 100644 --- a/src/Application/Repositories/IViewRepository.cs +++ b/src/Application/Repositories/IViewRepository.cs @@ -1,10 +1,8 @@ -using PoEGamblingHelper.Domain.Entity.Analytics; - -namespace PoEGamblingHelper.Application.Repositories; +namespace PoEGamblingHelper.Application.Repositories; public interface IViewRepository { - Task AddAsync(View view); + Task AddAsync(string ipAddress); Task CountViewsAsync(DateOnly date); Task RemoveAllAsync(DateOnly date); } \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index a1fb8d92..8863bf52 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -1,38 +1,34 @@ -using System.Security.Cryptography; -using System.Text; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Repositories; -using PoEGamblingHelper.Domain.Entity.Analytics; namespace PoEGamblingHelper.Application.Services; public class AnalyticsService : IAnalyticsService { private readonly IAnalyticsDayRepository _analyticsDayRepository; + private readonly IDateTimeService _dateTimeService; private readonly ILogger _logger; private readonly IViewRepository _viewRepository; public AnalyticsService(IViewRepository viewRepository, ILogger logger, - IAnalyticsDayRepository analyticsDayRepository) + IAnalyticsDayRepository analyticsDayRepository, + IDateTimeService dateTimeService) { _viewRepository = viewRepository; _logger = logger; _analyticsDayRepository = analyticsDayRepository; + _dateTimeService = dateTimeService; } public async Task AddView(string? ipAddress) { - if (ipAddress is null) return; - var ipHash = SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); - var today = DateOnly.FromDateTime(DateTime.UtcNow); - var view = new View { IpHash = ipHash, TimeStamp = today }; - await _viewRepository.AddAsync(view); + if (ipAddress is not null) await _viewRepository.AddAsync(ipAddress); } public async Task LogYesterdaysViews() { - var yesterday = DateOnly.FromDateTime(DateTime.UtcNow).AddDays(-1); + var yesterday = _dateTimeService.UtcToday().AddDays(-1); var viewCount = await _viewRepository.CountViewsAsync(yesterday); _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", diff --git a/src/Application/Services/IAnalyticsService.cs b/src/Application/Services/IAnalyticsService.cs index 2fd35187..92269c6f 100644 --- a/src/Application/Services/IAnalyticsService.cs +++ b/src/Application/Services/IAnalyticsService.cs @@ -1,6 +1,6 @@ namespace PoEGamblingHelper.Application.Services; -public interface IAnalyticsService //TODO rename +public interface IAnalyticsService { Task AddView(string? ipAddress); Task LogYesterdaysViews(); diff --git a/src/Application/Services/IDateTimeService.cs b/src/Application/Services/IDateTimeService.cs new file mode 100644 index 00000000..5421d8e2 --- /dev/null +++ b/src/Application/Services/IDateTimeService.cs @@ -0,0 +1,7 @@ +namespace PoEGamblingHelper.Application.Services; + +public interface IDateTimeService +{ + DateOnly UtcToday(); + DateTime UtcNow(); +} \ No newline at end of file diff --git a/src/Application/Services/IHashingService.cs b/src/Application/Services/IHashingService.cs new file mode 100644 index 00000000..cd2e61cf --- /dev/null +++ b/src/Application/Services/IHashingService.cs @@ -0,0 +1,6 @@ +namespace PoEGamblingHelper.Application.Services; + +public interface IHashingService +{ + byte[] HashIpAddress(string ipAddress); +} \ No newline at end of file diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs index 597aa3cb..8e052892 100644 --- a/src/Infrastructure/Repositories/ViewRepository.cs +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity.Analytics; using PoEGamblingHelper.Infrastructure.Database; @@ -7,15 +8,27 @@ namespace PoEGamblingHelper.Infrastructure.Repositories; public class ViewRepository : IViewRepository { + private readonly IDateTimeService _dateTimeService; private readonly IDbContextFactory _dbContextFactory; + private readonly IHashingService _hashingService; - public ViewRepository(IDbContextFactory dbContextFactory) + public ViewRepository(IDbContextFactory dbContextFactory, + IDateTimeService dateTimeService, + IHashingService hashingService) { _dbContextFactory = dbContextFactory; + _dateTimeService = dateTimeService; + _hashingService = hashingService; } - public async Task AddAsync(View view) + public async Task AddAsync(string ipAddress) { + var view = new View() + { + IpHash = _hashingService.HashIpAddress(ipAddress), + TimeStamp = _dateTimeService.UtcToday() + }; + await using var context = await _dbContextFactory.CreateDbContextAsync(); if (await context.View.AnyAsync(v => v.IpHash.Equals(view.IpHash) && v.TimeStamp == view.TimeStamp)) return; diff --git a/src/Infrastructure/Services/DateTimeService.cs b/src/Infrastructure/Services/DateTimeService.cs new file mode 100644 index 00000000..18a4ee0c --- /dev/null +++ b/src/Infrastructure/Services/DateTimeService.cs @@ -0,0 +1,10 @@ +using PoEGamblingHelper.Application.Services; + +namespace PoEGamblingHelper.Infrastructure.Services; + +public class DateTimeService : IDateTimeService +{ + public DateOnly UtcToday() { return DateOnly.FromDateTime(UtcNow()); } + + public DateTime UtcNow() { return DateTime.UtcNow; } +} \ No newline at end of file diff --git a/src/Infrastructure/Services/HashingService.cs b/src/Infrastructure/Services/HashingService.cs new file mode 100644 index 00000000..4793ecbd --- /dev/null +++ b/src/Infrastructure/Services/HashingService.cs @@ -0,0 +1,10 @@ +using System.Security.Cryptography; +using System.Text; +using PoEGamblingHelper.Application.Services; + +namespace PoEGamblingHelper.Infrastructure.Services; + +public class HashingService : IHashingService +{ + public byte[] HashIpAddress(string ipAddress) { return SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); } +} \ No newline at end of file diff --git a/test/Application.Test/Services/LeagueServiceTest.cs b/test/Application.Test/Services/LeagueServiceTest.cs index bb30f8c4..ca374519 100644 --- a/test/Application.Test/Services/LeagueServiceTest.cs +++ b/test/Application.Test/Services/LeagueServiceTest.cs @@ -4,7 +4,7 @@ using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; -namespace Application.Test.Services; +namespace PoEGamblingHelper.Application.Test.Services; public class LeagueServiceTest { From d560a05fd22dd8f52eb07c296b3b1d5a8d468b41 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:34:17 +0200 Subject: [PATCH 036/129] cleanup --- .../Repositories/IAnalyticsDayRepository.cs | 2 +- src/Application/Repositories/ILeagueRepository.cs | 2 +- src/Application/Services/AnalyticsService.cs | 2 +- src/Application/Services/LeagueService.cs | 10 ++++++---- src/Domain/Entity/Analytics/AnalyticsDay.cs | 2 +- .../Repositories/AnalyticsDayRepository.cs | 4 ++-- src/Infrastructure/Repositories/LeagueRepository.cs | 4 ++-- src/Infrastructure/ServiceCollectionExtensions.cs | 1 + 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Application/Repositories/IAnalyticsDayRepository.cs b/src/Application/Repositories/IAnalyticsDayRepository.cs index 1b7071f0..ece27d57 100644 --- a/src/Application/Repositories/IAnalyticsDayRepository.cs +++ b/src/Application/Repositories/IAnalyticsDayRepository.cs @@ -2,5 +2,5 @@ public interface IAnalyticsDayRepository { - Task AddAsync(int viewCount); + Task AddAsync(int viewCount, DateOnly date); } \ No newline at end of file diff --git a/src/Application/Repositories/ILeagueRepository.cs b/src/Application/Repositories/ILeagueRepository.cs index 66b516f6..3bc9ccc2 100644 --- a/src/Application/Repositories/ILeagueRepository.cs +++ b/src/Application/Repositories/ILeagueRepository.cs @@ -4,6 +4,6 @@ namespace PoEGamblingHelper.Application.Repositories; public interface ILeagueRepository { - League GetByStartDateAfter(DateTime dateTime); + League GetByStartDateBefore(DateTime dateTime); IAsyncEnumerable GetAllLeagues(); } \ No newline at end of file diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs index 8863bf52..f8458540 100644 --- a/src/Application/Services/AnalyticsService.cs +++ b/src/Application/Services/AnalyticsService.cs @@ -35,7 +35,7 @@ public async Task LogYesterdaysViews() viewCount, yesterday.Day, yesterday.Month, yesterday.Year); - await _analyticsDayRepository.AddAsync(viewCount); + await _analyticsDayRepository.AddAsync(viewCount, yesterday); await _viewRepository.RemoveAllAsync(yesterday); } } \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs index adec2282..545dddfd 100644 --- a/src/Application/Services/LeagueService.cs +++ b/src/Application/Services/LeagueService.cs @@ -5,14 +5,16 @@ namespace PoEGamblingHelper.Application.Services; public class LeagueService : ILeagueService { + private readonly IDateTimeService _dateTimeService; private readonly ILeagueRepository _leagueRepository; - public LeagueService(ILeagueRepository leagueRepository) { _leagueRepository = leagueRepository; } - public League GetCurrent() + public LeagueService(ILeagueRepository leagueRepository, IDateTimeService dateTimeService) { - var utcNow = DateTime.Today.ToUniversalTime(); //TODO DateTime Today in Infrastructure - return _leagueRepository.GetByStartDateAfter(utcNow); + _leagueRepository = leagueRepository; + _dateTimeService = dateTimeService; } + public League GetCurrent() { return _leagueRepository.GetByStartDateBefore(_dateTimeService.UtcNow()); } + public IAsyncEnumerable GetAll() { return _leagueRepository.GetAllLeagues(); } } \ No newline at end of file diff --git a/src/Domain/Entity/Analytics/AnalyticsDay.cs b/src/Domain/Entity/Analytics/AnalyticsDay.cs index 3bad25d0..37524fb5 100644 --- a/src/Domain/Entity/Analytics/AnalyticsDay.cs +++ b/src/Domain/Entity/Analytics/AnalyticsDay.cs @@ -5,6 +5,6 @@ namespace PoEGamblingHelper.Domain.Entity.Analytics; public class AnalyticsDay : Entity { - public DateOnly TimeStamp { get; set; } + public DateOnly Date { get; set; } public long Views { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/Repositories/AnalyticsDayRepository.cs b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs index 5c2eba85..de666e3d 100644 --- a/src/Infrastructure/Repositories/AnalyticsDayRepository.cs +++ b/src/Infrastructure/Repositories/AnalyticsDayRepository.cs @@ -14,12 +14,12 @@ public AnalyticsDayRepository(IDbContextFactory dbContextF _dbContextFactory = dbContextFactory; } - public async Task AddAsync(int viewCount) + public async Task AddAsync(int viewCount, DateOnly date) { var analyticsDay = new AnalyticsDay { Views = viewCount, - TimeStamp = DateOnly.FromDateTime(DateTime.UtcNow) //TODO remove UtcNow + Date = date }; await using var context = await _dbContextFactory.CreateDbContextAsync(); context.AnalyticsDay.Add(analyticsDay); diff --git a/src/Infrastructure/Repositories/LeagueRepository.cs b/src/Infrastructure/Repositories/LeagueRepository.cs index 289aeb32..91bad8cb 100644 --- a/src/Infrastructure/Repositories/LeagueRepository.cs +++ b/src/Infrastructure/Repositories/LeagueRepository.cs @@ -15,10 +15,10 @@ public LeagueRepository(IDbContextFactory dbContextFactory _dbContextFactory = dbContextFactory; } - public League GetByStartDateAfter(DateTime dateTime) + public League GetByStartDateBefore(DateTime dateTime) { using var applicationDbContext = _dbContextFactory.CreateDbContext(); - return applicationDbContext.League.Where(league => dateTime >= league.StartDate) + return applicationDbContext.League.Where(league => league.StartDate <= dateTime) .OrderByDescending(league => league.StartDate) .FirstOrDefault() ?? throw new NoLeagueDataException(); diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index ab0c7593..031b25de 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -24,6 +24,7 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddBackgroundJobs(); } From 8d1264797ef6f1b54bb3647fc963610a9aa8f6c3 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:37:43 +0200 Subject: [PATCH 037/129] remove useless service --- src/Api/Controllers/LeagueController.cs | 10 +++++----- .../Repositories/ILeagueRepository.cs | 1 + src/Application/Services/ILeagueService.cs | 9 --------- src/Application/Services/LeagueService.cs | 20 ------------------- .../BackgroundJobs/FetchPriceDataJob.cs | 11 +++++----- .../Repositories/LeagueRepository.cs | 7 ++++++- .../ServiceCollectionExtensions.cs | 1 - src/Web/Pages/GamblingHelper.razor.cs | 1 + .../Services/Implementations/GemService.cs | 1 + src/Web/Shared/Components/GemStats.razor | 3 ++- 10 files changed, 22 insertions(+), 42 deletions(-) delete mode 100644 src/Application/Services/ILeagueService.cs delete mode 100644 src/Application/Services/LeagueService.cs diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index 921b5eb6..7b2f334f 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -1,19 +1,19 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; -using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; namespace PoEGamblingHelper.Api.Controllers; public class LeagueController : ApiControllerBase { - private readonly ILeagueService _leagueService; + private readonly ILeagueRepository _leagueRepository; - public LeagueController(ILeagueService leagueService) { _leagueService = leagueService; } + public LeagueController(ILeagueRepository leagueRepository) { _leagueRepository = leagueRepository; } - [HttpGet] public IAsyncEnumerable GetAll() { return _leagueService.GetAll(); } + [HttpGet] public IAsyncEnumerable GetAll() { return _leagueRepository.GetAllLeagues(); } [HttpGet("current")] [OutputCache(PolicyName = "FetchData")] - public League GetCurrent() { return _leagueService.GetCurrent(); } + public League GetCurrent() { return _leagueRepository.GetCurrent(); } } \ No newline at end of file diff --git a/src/Application/Repositories/ILeagueRepository.cs b/src/Application/Repositories/ILeagueRepository.cs index 3bc9ccc2..1201431d 100644 --- a/src/Application/Repositories/ILeagueRepository.cs +++ b/src/Application/Repositories/ILeagueRepository.cs @@ -6,4 +6,5 @@ public interface ILeagueRepository { League GetByStartDateBefore(DateTime dateTime); IAsyncEnumerable GetAllLeagues(); + League GetCurrent(); } \ No newline at end of file diff --git a/src/Application/Services/ILeagueService.cs b/src/Application/Services/ILeagueService.cs deleted file mode 100644 index aefffdae..00000000 --- a/src/Application/Services/ILeagueService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using PoEGamblingHelper.Domain.Entity; - -namespace PoEGamblingHelper.Application.Services; - -public interface ILeagueService //TODO rename -{ - League GetCurrent(); - IAsyncEnumerable GetAll(); -} \ No newline at end of file diff --git a/src/Application/Services/LeagueService.cs b/src/Application/Services/LeagueService.cs deleted file mode 100644 index 545dddfd..00000000 --- a/src/Application/Services/LeagueService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using PoEGamblingHelper.Application.Repositories; -using PoEGamblingHelper.Domain.Entity; - -namespace PoEGamblingHelper.Application.Services; - -public class LeagueService : ILeagueService -{ - private readonly IDateTimeService _dateTimeService; - private readonly ILeagueRepository _leagueRepository; - - public LeagueService(ILeagueRepository leagueRepository, IDateTimeService dateTimeService) - { - _leagueRepository = leagueRepository; - _dateTimeService = dateTimeService; - } - - public League GetCurrent() { return _leagueRepository.GetByStartDateBefore(_dateTimeService.UtcNow()); } - - public IAsyncEnumerable GetAll() { return _leagueRepository.GetAllLeagues(); } -} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index 9ecb7a3d..ae7353d9 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Exception.Abstract; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; @@ -14,18 +15,18 @@ public class FetchPriceDataJob : BackgroundJob private readonly IDataFetchService _dataFetchService; private readonly TimeSpan _interval = TimeSpan.FromHours(6); //TODO - private readonly ILeagueService _leagueService; + private readonly ILeagueRepository _leagueRepository; private readonly ILogger _logger; public FetchPriceDataJob(ILogger logger, IDataFetchService dataFetchService, - ILeagueService leagueService, - IOutputCacheStore cache) + IOutputCacheStore cache, + ILeagueRepository leagueRepository) { _logger = logger; _dataFetchService = dataFetchService; - _leagueService = leagueService; _cache = cache; + _leagueRepository = leagueRepository; } protected override TimeSpan Interval() { return _interval; } @@ -35,7 +36,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) League league; try { - league = _leagueService.GetCurrent(); + league = _leagueRepository.GetCurrent(); } catch (NoLeagueDataException e) { diff --git a/src/Infrastructure/Repositories/LeagueRepository.cs b/src/Infrastructure/Repositories/LeagueRepository.cs index 91bad8cb..e3d9a3df 100644 --- a/src/Infrastructure/Repositories/LeagueRepository.cs +++ b/src/Infrastructure/Repositories/LeagueRepository.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Database; @@ -8,13 +9,17 @@ namespace PoEGamblingHelper.Infrastructure.Repositories; public class LeagueRepository : ILeagueRepository { + private readonly IDateTimeService _dateTimeService; private readonly IDbContextFactory _dbContextFactory; - public LeagueRepository(IDbContextFactory dbContextFactory) + public LeagueRepository(IDbContextFactory dbContextFactory, IDateTimeService dateTimeService) { _dbContextFactory = dbContextFactory; + _dateTimeService = dateTimeService; } + public League GetCurrent() { return GetByStartDateBefore(_dateTimeService.UtcNow()); } + public League GetByStartDateBefore(DateTime dateTime) { using var applicationDbContext = _dbContextFactory.CreateDbContext(); diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 031b25de..7413c6b0 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -17,7 +17,6 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddDatabase(configuration); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index ef286ce7..142b069a 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -1,6 +1,7 @@ using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; using PoEGamblingHelper.Web.Services.Interfaces; diff --git a/src/Web/Services/Implementations/GemService.cs b/src/Web/Services/Implementations/GemService.cs index 5ea10440..8790fde1 100644 --- a/src/Web/Services/Implementations/GemService.cs +++ b/src/Web/Services/Implementations/GemService.cs @@ -2,6 +2,7 @@ using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity.Gem; using PoEGamblingHelper.Web.Services.Interfaces; +using PoEGamblingHelper.Web.Util; namespace PoEGamblingHelper.Web.Services.Implementations; diff --git a/src/Web/Shared/Components/GemStats.razor b/src/Web/Shared/Components/GemStats.razor index 5c542b4c..8127e0cb 100644 --- a/src/Web/Shared/Components/GemStats.razor +++ b/src/Web/Shared/Components/GemStats.razor @@ -1,4 +1,5 @@ -@using PoEGamblingHelper.Domain.Entity.Gem +@using PoEGamblingHelper.Application.Extensions +@using PoEGamblingHelper.Domain.Entity.Gem
From 463e024be99daa9f0f1138ea88d5a374b2a3385f Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:41:51 +0200 Subject: [PATCH 038/129] remove useless service --- src/Api/Middleware/AnalyticsMiddleware.cs | 8 ++-- src/Application/Services/AnalyticsService.cs | 41 ------------------- src/Application/Services/IAnalyticsService.cs | 7 ---- .../BackgroundJobs/LogAnalyticsJob.cs | 29 +++++++++++-- .../ServiceCollectionExtensions.cs | 1 - 5 files changed, 30 insertions(+), 56 deletions(-) delete mode 100644 src/Application/Services/AnalyticsService.cs delete mode 100644 src/Application/Services/IAnalyticsService.cs diff --git a/src/Api/Middleware/AnalyticsMiddleware.cs b/src/Api/Middleware/AnalyticsMiddleware.cs index e5270bf6..bb1bc4f8 100644 --- a/src/Api/Middleware/AnalyticsMiddleware.cs +++ b/src/Api/Middleware/AnalyticsMiddleware.cs @@ -1,5 +1,5 @@ using PoEGamblingHelper.Api.Extensions; -using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Application.Repositories; namespace PoEGamblingHelper.Api.Middleware; @@ -9,9 +9,11 @@ public class AnalyticsMiddleware public AnalyticsMiddleware(RequestDelegate next) { _next = next; } - public async Task InvokeAsync(HttpContext httpContext, IAnalyticsService analyticsService) + public async Task InvokeAsync(HttpContext httpContext, IViewRepository viewRepository) { - await analyticsService.AddView(httpContext.Request.GetRealIpAddress()); + var ip = httpContext.Request.GetRealIpAddress(); + if (ip is not null) await viewRepository.AddAsync(ip); + await _next(httpContext); } } diff --git a/src/Application/Services/AnalyticsService.cs b/src/Application/Services/AnalyticsService.cs deleted file mode 100644 index f8458540..00000000 --- a/src/Application/Services/AnalyticsService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.Extensions.Logging; -using PoEGamblingHelper.Application.Repositories; - -namespace PoEGamblingHelper.Application.Services; - -public class AnalyticsService : IAnalyticsService -{ - private readonly IAnalyticsDayRepository _analyticsDayRepository; - private readonly IDateTimeService _dateTimeService; - private readonly ILogger _logger; - private readonly IViewRepository _viewRepository; - - public AnalyticsService(IViewRepository viewRepository, - ILogger logger, - IAnalyticsDayRepository analyticsDayRepository, - IDateTimeService dateTimeService) - { - _viewRepository = viewRepository; - _logger = logger; - _analyticsDayRepository = analyticsDayRepository; - _dateTimeService = dateTimeService; - } - - public async Task AddView(string? ipAddress) - { - if (ipAddress is not null) await _viewRepository.AddAsync(ipAddress); - } - - public async Task LogYesterdaysViews() - { - var yesterday = _dateTimeService.UtcToday().AddDays(-1); - - var viewCount = await _viewRepository.CountViewsAsync(yesterday); - _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", - viewCount, yesterday.Day, yesterday.Month, yesterday.Year); - - - await _analyticsDayRepository.AddAsync(viewCount, yesterday); - await _viewRepository.RemoveAllAsync(yesterday); - } -} \ No newline at end of file diff --git a/src/Application/Services/IAnalyticsService.cs b/src/Application/Services/IAnalyticsService.cs deleted file mode 100644 index 92269c6f..00000000 --- a/src/Application/Services/IAnalyticsService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace PoEGamblingHelper.Application.Services; - -public interface IAnalyticsService -{ - Task AddView(string? ipAddress); - Task LogYesterdaysViews(); -} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs index 26d4382c..cb46d4bd 100644 --- a/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs +++ b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs @@ -1,19 +1,40 @@ -using PoEGamblingHelper.Application.Services; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.Services; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; public class LogAnalyticsJob : BackgroundJob { - private readonly IAnalyticsService _analyticsService; + private readonly IAnalyticsDayRepository _analyticsDayRepository; + private readonly IDateTimeService _dateTimeService; private readonly TimeSpan _interval = TimeSpan.FromDays(1); + private readonly ILogger _logger; + private readonly IViewRepository _viewRepository; - public LogAnalyticsJob(IAnalyticsService analyticsService) { _analyticsService = analyticsService; } + public LogAnalyticsJob(IDateTimeService dateTimeService, + IViewRepository viewRepository, + ILogger logger, + IAnalyticsDayRepository analyticsDayRepository) + { + _dateTimeService = dateTimeService; + _viewRepository = viewRepository; + _logger = logger; + _analyticsDayRepository = analyticsDayRepository; + } protected override TimeSpan Interval() { return _interval; } protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { - await _analyticsService.LogYesterdaysViews(); + var yesterday = _dateTimeService.UtcToday().AddDays(-1); + + var views = await _viewRepository.CountViewsAsync(yesterday); + _logger.LogInformation("{Views} People used the website on the {YesterdayDay}.{YesterdayMonth}.{YesterdayYear}", + views, yesterday.Day, yesterday.Month, yesterday.Year); + + await _analyticsDayRepository.AddAsync(views, yesterday); + await _viewRepository.RemoveAllAsync(yesterday); } } \ No newline at end of file diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 7413c6b0..00349e3c 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -18,7 +18,6 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); From 1915fa34ab47d0cb70a22c11ed5a48439d576a52 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 18:45:11 +0200 Subject: [PATCH 039/129] cleanup --- src/Application/Extensions/GemDataExtensions.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index cbe0599f..be36815e 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -13,15 +13,10 @@ public static decimal AvgProfitPerTry(this GemData gemData, decimal? bestCaseValue = null, decimal templeCost = 0) { - var worstCaseProfit = worstCaseValue is null - ? gemData.Profit(ResultCase.Worst, rawCost, templeCost) - : gemData.Profit((decimal)worstCaseValue, rawCost, templeCost); - var middleCaseProfit = middleCaseValue is null - ? gemData.Profit(ResultCase.Middle, rawCost, templeCost) - : gemData.Profit((decimal)middleCaseValue, rawCost, templeCost); - var bestCaseProfit = bestCaseValue is null - ? gemData.Profit(ResultCase.Best, rawCost, templeCost) - : gemData.Profit((decimal)bestCaseValue, rawCost, templeCost); + var worstCaseProfit = gemData.Profit(worstCaseValue ?? gemData.Value(ResultCase.Worst), rawCost, templeCost); + var middleCaseProfit = gemData.Profit(middleCaseValue ?? gemData.Value(ResultCase.Middle), rawCost, templeCost); + var bestCaseProfit = gemData.Profit(bestCaseValue ?? gemData.Value(ResultCase.Best), rawCost, templeCost); + return (worstCaseProfit + 2 * middleCaseProfit + bestCaseProfit) / 4; } From 25b222d963902b6c409d2ed96bf0d2c27368179c Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 19:01:39 +0200 Subject: [PATCH 040/129] cleanup --- .../Extensions/GemDataExtensions.cs | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index be36815e..0b84448a 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -29,55 +29,61 @@ public static decimal Profit(this GemData gemData, } private static decimal Profit(this GemData gemData, - decimal value, + decimal resultValue, decimal? rawCost = null, decimal templeCost = 0) { - return value - gemData.CostPerTry(rawCost, templeCost); + return resultValue - gemData.CostPerTry(rawCost, templeCost); } + /// + /// Finds the ChaosValue of the Gem for the specified resultCase. + /// If there are multiple, it just gets the cheapest one. + /// private static decimal Value(this GemData gemData, ResultCase resultCase) { return gemData.ResultValue(gemData.MaxLevel() + resultCase.LevelModifier()); } + /// + /// Finds the ChaosValue of the Gem with the specified level and corrupted. + /// If there are multiple, it just gets the cheapest one. + /// private static decimal ResultValue(this GemData gemData, int level) { - return gemData.Gems.Where(gem => gem.GemLevel == level && gem.Corrupted).MinBy(gem => gem.GemQuality) + return gemData.Gems + .Where(gem => gem.GemLevel == level && gem.Corrupted) + .MinBy(gem => gem.ChaosValue) ?.ChaosValue ?? 0m; } public static int MaxLevel(this GemData gemData) - { - var isAwakened = gemData.Name.ToLowerInvariant().Contains("awakened"); - var isExceptional = gemData.IsExceptional(); + { //TODO this is incorrect, I need to pull this data from somewhere + var lowerName = gemData.Name.ToLowerInvariant(); + var isAwakened = lowerName.Contains("awakened"); + var isExceptional = lowerName.Contains("enhance") + || lowerName.Contains("empower") + || lowerName.Contains("enlighten"); return isAwakened && isExceptional ? 4 : isExceptional ? 3 : isAwakened ? 5 : 20; } - private static bool IsExceptional(this GemData gemData) - { - var lowerName = gemData.Name.ToLowerInvariant(); - return lowerName.Contains("enhance") || lowerName.Contains("empower") || lowerName.Contains("enlighten"); - } - - private static decimal CostPerTry(this GemData gemData, - decimal? rawCost = null, - decimal templeCost = 0) + private static decimal CostPerTry(this GemData gemData, decimal? rawCost = null, decimal templeCost = 0) { return (rawCost ?? gemData.RawCost()) + templeCost; } + /// + /// Finds the ChaosValue of the Gem with maximum level and not corrupted. + /// If there are multiple, it just gets the cheapest one. + /// public static decimal RawCost(this GemData gemData) { - return gemData.Gems.Where(gem => gem.GemLevel == gemData.MaxLevel() && !gem.Corrupted) - .MinBy(gem => gem.GemQuality)?.ChaosValue ?? 0m; - } - - public static string ToString(this GemData gemData) - { - return $"[Id={gemData.Id}, Name={gemData.Name}, MaxLevel={gemData.MaxLevel()}]"; + return gemData.Gems + .Where(gem => gem.GemLevel == gemData.MaxLevel() && !gem.Corrupted) + .MinBy(gem => gem.ChaosValue)? + .ChaosValue ?? 0m; } } \ No newline at end of file From f8be78c3d500a760e932b3f97c7a8b73384da531 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 19:02:46 +0200 Subject: [PATCH 041/129] cleanup --- src/Application/QueryParameters/PageRequest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Application/QueryParameters/PageRequest.cs b/src/Application/QueryParameters/PageRequest.cs index cb445385..702f0b72 100644 --- a/src/Application/QueryParameters/PageRequest.cs +++ b/src/Application/QueryParameters/PageRequest.cs @@ -2,13 +2,13 @@ public class PageRequest { - private int _pageSize; - public int PageNumber { get; set; } + private readonly int _pageSize; + public int PageNumber { get; init; } public int PageSize { get => _pageSize; - set => _pageSize = value <= 0 ? 0 : value; + init => _pageSize = value <= 0 ? 0 : value; } public (int skipSize, int takeSize) ConvertToSizes() { return (PageSize * PageNumber, PageSize); } From 73f3e93176a45f613de3ac2ff0232d8811ce3265 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 19:49:39 +0200 Subject: [PATCH 042/129] cleanup GemRepositroy --- src/Api/Controllers/GemController.cs | 8 +-- src/Application/QueryParameters/Page.cs | 8 ++- .../QueryParameters/PageRequest.cs | 2 - .../Repositories/IGemRepository.cs | 6 +- src/Application/Services/IGemService.cs | 9 --- .../BackgroundJobs/FetchLeagueJob.cs | 10 +-- .../BackgroundJobs/FetchPriceDataJob.cs | 14 ++-- .../ServiceCollectionExtensions.cs | 5 +- .../{DataFetchService.cs => DataFetcher.cs} | 11 ++-- .../{GemService.cs => GemRepository.cs} | 66 +++++++++---------- .../Services/IDataFetcher.cs} | 4 +- 11 files changed, 65 insertions(+), 78 deletions(-) delete mode 100644 src/Application/Services/IGemService.cs rename src/Infrastructure/Services/{DataFetchService.cs => DataFetcher.cs} (97%) rename src/Infrastructure/Services/{GemService.cs => GemRepository.cs} (70%) rename src/{Application/Services/IDataFetchService.cs => Infrastructure/Services/IDataFetcher.cs} (67%) diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index fb05c648..f0af180d 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -1,21 +1,21 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Application.QueryParameters; -using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Gem; namespace PoEGamblingHelper.Api.Controllers; public class GemController : ApiControllerBase { - private readonly IGemService _gemService; + private readonly IGemRepository _gemRepository; - public GemController(IGemService gemService) { _gemService = gemService; } + public GemController(IGemRepository gemRepository) { _gemRepository = gemRepository; } [HttpGet] [OutputCache(PolicyName = "FetchData")] public async Task> GetAll([FromQuery] GemDataQuery? query, [FromQuery] PageRequest page) { - return await _gemService.GetAll(query, page); + return await _gemRepository.Search(query, page); } } \ No newline at end of file diff --git a/src/Application/QueryParameters/Page.cs b/src/Application/QueryParameters/Page.cs index a51f2ae4..f66d693c 100644 --- a/src/Application/QueryParameters/Page.cs +++ b/src/Application/QueryParameters/Page.cs @@ -1,8 +1,10 @@ -namespace PoEGamblingHelper.Application.QueryParameters; +using System.Collections.Immutable; -public class Page +namespace PoEGamblingHelper.Application.QueryParameters; + +public record Page { - public IEnumerable Content { get; init; } = null!; + public IImmutableList Content { get; init; } = null!; public int CurrentPage { get; init; } public bool LastPage { get; init; } } \ No newline at end of file diff --git a/src/Application/QueryParameters/PageRequest.cs b/src/Application/QueryParameters/PageRequest.cs index 702f0b72..075daab6 100644 --- a/src/Application/QueryParameters/PageRequest.cs +++ b/src/Application/QueryParameters/PageRequest.cs @@ -10,6 +10,4 @@ public int PageSize get => _pageSize; init => _pageSize = value <= 0 ? 0 : value; } - - public (int skipSize, int takeSize) ConvertToSizes() { return (PageSize * PageNumber, PageSize); } } \ No newline at end of file diff --git a/src/Application/Repositories/IGemRepository.cs b/src/Application/Repositories/IGemRepository.cs index 85219006..030fdbb4 100644 --- a/src/Application/Repositories/IGemRepository.cs +++ b/src/Application/Repositories/IGemRepository.cs @@ -1,5 +1,9 @@ -namespace PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.QueryParameters; +using PoEGamblingHelper.Domain.Entity.Gem; + +namespace PoEGamblingHelper.Application.Repositories; public interface IGemRepository { + Task> Search(GemDataQuery? query, PageRequest page); } \ No newline at end of file diff --git a/src/Application/Services/IGemService.cs b/src/Application/Services/IGemService.cs deleted file mode 100644 index c7a39996..00000000 --- a/src/Application/Services/IGemService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using PoEGamblingHelper.Application.QueryParameters; -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Application.Services; - -public interface IGemService //TODO rename -{ - Task> GetAll(GemDataQuery? query, PageRequest page); -} \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs index 7bf4a763..fe338d06 100644 --- a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs @@ -1,20 +1,20 @@ using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception.Abstract; -using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Infrastructure.Services; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; public class FetchLeagueJob : BackgroundJob { - private readonly IDataFetchService _dataFetchService; + private readonly IDataFetcher _dataFetcher; private readonly TimeSpan _interval = TimeSpan.FromHours(6); private readonly ILogger _logger; - public FetchLeagueJob(ILogger logger, IDataFetchService dataFetchService) + public FetchLeagueJob(ILogger logger, IDataFetcher dataFetcher) { _logger = logger; - _dataFetchService = dataFetchService; + _dataFetcher = dataFetcher; } protected override TimeSpan Interval() { return _interval; } @@ -23,7 +23,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { try { - await _dataFetchService.FetchCurrentLeague(); + await _dataFetcher.FetchCurrentLeague(); } catch (PoeGamblingHelperException e) { diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index ae7353d9..e8caad24 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -3,8 +3,8 @@ using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Exception.Abstract; using PoEGamblingHelper.Application.Repositories; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Services; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; @@ -12,19 +12,19 @@ public class FetchPriceDataJob : BackgroundJob { private readonly IOutputCacheStore _cache; private readonly string _cacheTag = ""; //TODO - private readonly IDataFetchService _dataFetchService; + private readonly IDataFetcher _dataFetcher; private readonly TimeSpan _interval = TimeSpan.FromHours(6); //TODO private readonly ILeagueRepository _leagueRepository; private readonly ILogger _logger; public FetchPriceDataJob(ILogger logger, - IDataFetchService dataFetchService, + IDataFetcher dataFetcher, IOutputCacheStore cache, ILeagueRepository leagueRepository) { _logger = logger; - _dataFetchService = dataFetchService; + _dataFetcher = dataFetcher; _cache = cache; _leagueRepository = leagueRepository; } @@ -48,7 +48,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) try { - await _dataFetchService.FetchCurrencyData(league); + await _dataFetcher.FetchCurrencyData(league); } catch (PoeGamblingHelperException e) { @@ -57,7 +57,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) try { - await _dataFetchService.FetchTemplePriceData(league); + await _dataFetcher.FetchTemplePriceData(league); } catch (PoeGamblingHelperException e) { @@ -66,7 +66,7 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) try { - await _dataFetchService.FetchGemPriceData(league); + await _dataFetcher.FetchGemPriceData(league); } catch (PoeGamblingHelperException e) { diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 00349e3c..4e1c37ff 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using PoEGamblingHelper.Application.Repositories; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure.BackgroundJobs; using PoEGamblingHelper.Infrastructure.Database; using PoEGamblingHelper.Infrastructure.Repositories; @@ -16,8 +15,8 @@ public static void AddInfrastructureServices(this IServiceCollection services, I { services.AddDatabase(configuration); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Infrastructure/Services/DataFetchService.cs b/src/Infrastructure/Services/DataFetcher.cs similarity index 97% rename from src/Infrastructure/Services/DataFetchService.cs rename to src/Infrastructure/Services/DataFetcher.cs index 7e16eb0b..e0af7ec7 100644 --- a/src/Infrastructure/Services/DataFetchService.cs +++ b/src/Infrastructure/Services/DataFetcher.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Extensions; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Database; using PoEGamblingHelper.Infrastructure.Services.FetchDtos; @@ -15,18 +14,18 @@ namespace PoEGamblingHelper.Infrastructure.Services; -public partial class DataFetchService : IDataFetchService +public partial class DataFetcher : IDataFetcher { private readonly IDbContextFactory _applicationDbContextFactory; private readonly HtmlWeb _htmlLoader = new(); private readonly IHttpClientFactory _httpClientFactory; private readonly MediaTypeHeaderValue _jsonMediaTypeHeader = MediaTypeHeaderValue.Parse("application/json"); - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly string _templeQuery; - public DataFetchService(ILogger logger, - IDbContextFactory applicationDbContextFactory, - IHttpClientFactory httpClientFactory) + public DataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory, + IHttpClientFactory httpClientFactory) { _logger = logger; _applicationDbContextFactory = applicationDbContextFactory; diff --git a/src/Infrastructure/Services/GemService.cs b/src/Infrastructure/Services/GemRepository.cs similarity index 70% rename from src/Infrastructure/Services/GemService.cs rename to src/Infrastructure/Services/GemRepository.cs index ea8497ea..7db82b46 100644 --- a/src/Infrastructure/Services/GemService.cs +++ b/src/Infrastructure/Services/GemRepository.cs @@ -1,51 +1,35 @@ -using System.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore; using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.QueryParameters; -using PoEGamblingHelper.Application.Services; +using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Gem; using PoEGamblingHelper.Infrastructure.Database; namespace PoEGamblingHelper.Infrastructure.Services; -public partial class GemService : IGemService +public partial class GemRepository : IGemRepository { private readonly IDbContextFactory _dbContextFactory; + private readonly ITempleRepository _templeRepository; - public GemService(IDbContextFactory dbContextFactory) + public GemRepository(IDbContextFactory dbContextFactory) { _dbContextFactory = dbContextFactory; } - public async Task> GetAll(GemDataQuery? query, PageRequest page) + public async Task> Search(GemDataQuery? query, PageRequest page) { if (query is null) return await GetAll(page); - - var allFoundGems = FilterGemData(query); - var (skipSize, takeSize) = page.ConvertToSizes(); - - return new Page - { - Content = allFoundGems.Skip(skipSize).Take(takeSize), - LastPage = skipSize + takeSize >= allFoundGems.Length, - CurrentPage = page.PageNumber - }; + return GeneratePage(FilterGemData(query), page); } private async Task> GetAll(PageRequest page) { - await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync(); - - var allContentLength = await applicationDbContext.GemData.CountAsync(); - var (skipSize, takeSize) = page.ConvertToSizes(); - - return new Page - { - Content = applicationDbContext.GemData.Skip(skipSize).Take(takeSize), - LastPage = skipSize + takeSize >= allContentLength, - CurrentPage = page.PageNumber - }; + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return GeneratePage(await context.GemData.ToArrayAsync(), page); } private GemData[] FilterGemData(GemDataQuery query) @@ -53,14 +37,12 @@ private GemData[] FilterGemData(GemDataQuery query) query.SearchText = SqlSanitizeRegex().Replace(query.SearchText, "").ToLowerInvariant(); query.PricePerTryFrom ??= decimal.MinValue; query.PricePerTryTo ??= decimal.MaxValue; + var preFilterSqlQuery = PreFilterSqlQuery(query); + var templeCost = _templeRepository.GetCurrent().AverageChaosValue(); using var applicationDbContext = _dbContextFactory.CreateDbContext(); - var templeCost = applicationDbContext.TempleCost - .OrderByDescending(cost => cost.TimeStamp) - .FirstOrDefault() - ?.AverageChaosValue() ?? 0; return applicationDbContext.GemData - .FromSqlRaw(PreFilterGemDataQuery(query)) + .FromSqlRaw(preFilterSqlQuery) .Include(gemData => gemData.Gems) .AsEnumerable() .Where(gemData => PostFilterGemData(gemData, query, templeCost)) @@ -68,7 +50,7 @@ private GemData[] FilterGemData(GemDataQuery query) .ToArray(); } - private static string PreFilterGemDataQuery(GemDataQuery query) + private static string PreFilterSqlQuery(GemDataQuery query) { const string isAlternateQuality = """ (LOWER("Name") LIKE 'anomalous%' @@ -105,8 +87,9 @@ WHERE LOWER("Name") LIKE '%{query.SearchText}%' private static bool PostFilterGemData(GemData gemData, GemDataQuery query, decimal averageTempleCost) { - return gemData.RawCost() >= query.PricePerTryFrom - && gemData.RawCost() <= query.PricePerTryTo + var cost = gemData.RawCost(); + return cost >= query.PricePerTryFrom + && cost <= query.PricePerTryTo && (!query.OnlyShowProfitable || gemData.AvgProfitPerTry(templeCost: averageTempleCost) > 0); } @@ -118,11 +101,22 @@ private static decimal OrderGemData(GemData gemData, Sort sort, decimal averageT Sort.CostPerTryDesc => -gemData.RawCost(), Sort.AverageProfitPerTryAsc => gemData.AvgProfitPerTry(templeCost: averageTempleCost), Sort.AverageProfitPerTryDesc => -gemData.AvgProfitPerTry(templeCost: averageTempleCost), - Sort.MaxProfitPerTryAsc => -gemData.Profit(ResultCase.Best, templeCost: averageTempleCost), + Sort.MaxProfitPerTryAsc => gemData.Profit(ResultCase.Best, templeCost: averageTempleCost), Sort.MaxProfitPerTryDesc => -gemData.Profit(ResultCase.Best, templeCost: averageTempleCost), - _ => (decimal)Random.Shared.NextDouble() + _ => 0m }; } [GeneratedRegex("[^a-z ]")] private static partial Regex SqlSanitizeRegex(); + + private static Page GeneratePage(IReadOnlyCollection gemData, PageRequest page) + { + var skipSize = page.PageSize * page.PageNumber; + return new Page + { + Content = gemData.Skip(skipSize).Take(page.PageSize).ToImmutableList(), + LastPage = skipSize + page.PageSize >= gemData.Count, + CurrentPage = page.PageNumber + }; + } } \ No newline at end of file diff --git a/src/Application/Services/IDataFetchService.cs b/src/Infrastructure/Services/IDataFetcher.cs similarity index 67% rename from src/Application/Services/IDataFetchService.cs rename to src/Infrastructure/Services/IDataFetcher.cs index 7c1e201e..9cf51e0d 100644 --- a/src/Application/Services/IDataFetchService.cs +++ b/src/Infrastructure/Services/IDataFetcher.cs @@ -1,8 +1,8 @@ using PoEGamblingHelper.Domain.Entity; -namespace PoEGamblingHelper.Application.Services; +namespace PoEGamblingHelper.Infrastructure.Services; -public interface IDataFetchService //TODO rename +public interface IDataFetcher //TODO rename { Task FetchCurrentLeague(); Task FetchCurrencyData(League league); From 5584d4dc2650563870e5f055129552a8d4378dd9 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 19:50:01 +0200 Subject: [PATCH 043/129] move class --- .../{Services => Repositories}/GemRepository.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename src/Infrastructure/{Services => Repositories}/GemRepository.cs (97%) diff --git a/src/Infrastructure/Services/GemRepository.cs b/src/Infrastructure/Repositories/GemRepository.cs similarity index 97% rename from src/Infrastructure/Services/GemRepository.cs rename to src/Infrastructure/Repositories/GemRepository.cs index 7db82b46..96d5c8fb 100644 --- a/src/Infrastructure/Services/GemRepository.cs +++ b/src/Infrastructure/Repositories/GemRepository.cs @@ -8,16 +8,17 @@ using PoEGamblingHelper.Domain.Entity.Gem; using PoEGamblingHelper.Infrastructure.Database; -namespace PoEGamblingHelper.Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.Repositories; public partial class GemRepository : IGemRepository { private readonly IDbContextFactory _dbContextFactory; private readonly ITempleRepository _templeRepository; - public GemRepository(IDbContextFactory dbContextFactory) + public GemRepository(IDbContextFactory dbContextFactory, ITempleRepository templeRepository) { _dbContextFactory = dbContextFactory; + _templeRepository = templeRepository; } public async Task> Search(GemDataQuery? query, PageRequest page) From 115f9ee90d60589634be2e922df7670e597913f8 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 19:50:13 +0200 Subject: [PATCH 044/129] man --- src/Infrastructure/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 4e1c37ff..ce525948 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -15,7 +15,7 @@ public static void AddInfrastructureServices(this IServiceCollection services, I { services.AddDatabase(configuration); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); From cadc3ec43aa220be0c80dd6334dc528df2fc4b7a Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 20:48:03 +0200 Subject: [PATCH 045/129] fix BackgroundJobs --- src/Api/appsettings.json | 9 ++++- .../BackgroundJobs/BackgroundJob.cs | 36 +++++++++++++------ .../BackgroundJobs/FetchLeagueJob.cs | 11 +++--- .../BackgroundJobs/FetchPriceDataJob.cs | 8 ++--- .../BackgroundJobs/LogAnalyticsJob.cs | 11 +++--- .../ServiceCollectionExtensions.cs | 12 +++++-- src/Infrastructure/Util/ExtensionMethods.cs | 18 +++++----- 7 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/Api/appsettings.json b/src/Api/appsettings.json index e64f8b9e..88337543 100644 --- a/src/Api/appsettings.json +++ b/src/Api/appsettings.json @@ -6,7 +6,8 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "Warning", + "System.Net.Http.HttpClient.Default": "Warning" } }, "AllowedHosts": "*", @@ -27,5 +28,11 @@ "ReplenishmentPeriodSeconds": 60, "TokensPerPeriod": 50 } + }, + "BackgroundJobIntervals": { + "FetchLeagueJob": "06:00:00", + "FetchPriceDataJob": "00:05:00", + "LogAnalyticsJob": "1.00:00:00", + "LogAnalyticsJobStartTime": "00:05:00" } } diff --git a/src/Infrastructure/BackgroundJobs/BackgroundJob.cs b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs index 18b62ac2..115fc7db 100644 --- a/src/Infrastructure/BackgroundJobs/BackgroundJob.cs +++ b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs @@ -1,25 +1,37 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using PoEGamblingHelper.Application.Services; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; public abstract class BackgroundJob : BackgroundService { private readonly TimeSpan _delay; + private readonly TimeSpan _interval; - protected BackgroundJob() { _delay = TimeSpan.Zero; } - protected BackgroundJob(TimeSpan delay) { _delay = delay; } - - protected BackgroundJob(TimeOnly startTime) + /// + /// Gets the StartTime from the configuration as well. + /// + protected BackgroundJob(IConfiguration configuration, IDateTimeService dateTimeService) : this(configuration) { - // now 23Uhr, start: 13uhr - // 13:00 - 23:00 = -10h WRONG -> + 24 + //TODO write unit tests + // start: 13uhr, now: 23Uhr // 13:00 - 03:00 = 10h RIGHT - _delay = startTime.ToTimeSpan().Subtract(DateTime.UtcNow.TimeOfDay); + // 13:00 - 23:00 = -10h WRONG -> + 24 = 14 + var startTime = configuration.GetSection("BackgroundJobIntervals") + .GetValue(GetType().Name + "StartTime"); + _delay = startTime.ToTimeSpan().Subtract(dateTimeService.UtcNow().TimeOfDay); if (_delay < TimeSpan.Zero) _delay = _delay.Add(TimeSpan.FromHours(24)); } - protected abstract TimeSpan Interval(); - protected abstract Task ExecuteJobAsync(CancellationToken stoppingToken); + protected BackgroundJob(IConfiguration configuration) : this(TimeSpan.Zero, configuration) { } + + private BackgroundJob(TimeSpan delay, IConfiguration configuration) + { + _delay = delay; + _interval = configuration.GetSection("BackgroundJobIntervals") + .GetValue(GetType().Name); + } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { @@ -27,7 +39,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) while (!stoppingToken.IsCancellationRequested) { await ExecuteJobAsync(stoppingToken); - await Task.Delay(Interval(), stoppingToken); + await Task.Delay(_interval, stoppingToken); } } + + protected abstract Task ExecuteJobAsync(CancellationToken stoppingToken); } \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs index fe338d06..fa9a1d98 100644 --- a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception.Abstract; using PoEGamblingHelper.Infrastructure.Services; @@ -7,18 +8,16 @@ namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; public class FetchLeagueJob : BackgroundJob { private readonly IDataFetcher _dataFetcher; - - private readonly TimeSpan _interval = TimeSpan.FromHours(6); private readonly ILogger _logger; - public FetchLeagueJob(ILogger logger, IDataFetcher dataFetcher) + public FetchLeagueJob(ILogger logger, + IDataFetcher dataFetcher, + IConfiguration configuration) : base(configuration) { _logger = logger; _dataFetcher = dataFetcher; } - protected override TimeSpan Interval() { return _interval; } - protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { try diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index e8caad24..ff0ef766 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.OutputCaching; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Exception.Abstract; @@ -13,15 +14,14 @@ public class FetchPriceDataJob : BackgroundJob private readonly IOutputCacheStore _cache; private readonly string _cacheTag = ""; //TODO private readonly IDataFetcher _dataFetcher; - - private readonly TimeSpan _interval = TimeSpan.FromHours(6); //TODO private readonly ILeagueRepository _leagueRepository; private readonly ILogger _logger; public FetchPriceDataJob(ILogger logger, IDataFetcher dataFetcher, IOutputCacheStore cache, - ILeagueRepository leagueRepository) + ILeagueRepository leagueRepository, + IConfiguration configuration) : base(configuration) { _logger = logger; _dataFetcher = dataFetcher; @@ -29,8 +29,6 @@ public FetchPriceDataJob(ILogger logger, _leagueRepository = leagueRepository; } - protected override TimeSpan Interval() { return _interval; } - protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { League league; diff --git a/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs index cb46d4bd..79dbf6c3 100644 --- a/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs +++ b/src/Infrastructure/BackgroundJobs/LogAnalyticsJob.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Application.Services; @@ -8,15 +9,15 @@ public class LogAnalyticsJob : BackgroundJob { private readonly IAnalyticsDayRepository _analyticsDayRepository; private readonly IDateTimeService _dateTimeService; - - private readonly TimeSpan _interval = TimeSpan.FromDays(1); private readonly ILogger _logger; private readonly IViewRepository _viewRepository; public LogAnalyticsJob(IDateTimeService dateTimeService, IViewRepository viewRepository, ILogger logger, - IAnalyticsDayRepository analyticsDayRepository) + IAnalyticsDayRepository analyticsDayRepository, + IConfiguration configuration) + : base(configuration, dateTimeService) { _dateTimeService = dateTimeService; _viewRepository = viewRepository; @@ -24,8 +25,6 @@ public LogAnalyticsJob(IDateTimeService dateTimeService, _analyticsDayRepository = analyticsDayRepository; } - protected override TimeSpan Interval() { return _interval; } - protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { var yesterday = _dateTimeService.UtcToday().AddDays(-1); diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index ce525948..f9f449ef 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using PoEGamblingHelper.Application.Repositories; +using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure.BackgroundJobs; using PoEGamblingHelper.Infrastructure.Database; using PoEGamblingHelper.Infrastructure.Repositories; @@ -15,13 +16,18 @@ public static void AddInfrastructureServices(this IServiceCollection services, I { services.AddDatabase(configuration); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddHttpClient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddBackgroundJobs(); } diff --git a/src/Infrastructure/Util/ExtensionMethods.cs b/src/Infrastructure/Util/ExtensionMethods.cs index 175b1426..bc38eebf 100644 --- a/src/Infrastructure/Util/ExtensionMethods.cs +++ b/src/Infrastructure/Util/ExtensionMethods.cs @@ -6,6 +6,15 @@ namespace PoEGamblingHelper.Infrastructure.Util; public static class ExtensionMethods { + public static GemData ToGemData(this IGrouping group, + IEnumerable gemTradeData, + IEnumerable existingGemData) + { + var result = ToGemData(group, gemTradeData); + result.Id = existingGemData.First(gem => gem.Name.EqualsIgnoreCase(group.Key)).Id; + return result; + } + public static GemData ToGemData(this IGrouping group, IEnumerable gemTradeData) { @@ -16,13 +25,4 @@ public static GemData ToGemData(this IGrouping group, Gems = gemTradeData.Where(tradeData => tradeData.Name.EqualsIgnoreCase(group.Key)).ToList() }; } - - public static GemData ToGemData(this IGrouping group, - IEnumerable gemTradeData, - IEnumerable existingGemData) - { - var result = ToGemData(group, gemTradeData); - result.Id = existingGemData.First(gem => gem.Name.EqualsIgnoreCase(group.Key)).Id; - return result; - } } \ No newline at end of file From 0bf65efd7a89f9a92be1e5bafee91d9579e4dd3e Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 20:56:17 +0200 Subject: [PATCH 046/129] Cleanup Database --- .../Configuration/TempleCostConfiguration.cs | 18 -- src/Infrastructure/Infrastructure.csproj | 3 + .../Migrations/20230216010517_1.0.Designer.cs | 170 ---------- .../Migrations/20230216010517_1.0.cs | 121 ------- .../Migrations/20230309081719_2.0.Designer.cs | 229 ------------- .../Migrations/20230309081719_2.0.cs | 73 ----- .../Migrations/20230419193237_1.1.Designer.cs | 183 ----------- .../Migrations/20230419193237_1.1.cs | 34 -- .../Migrations/20230420132741_1.2.Designer.cs | 188 ----------- .../Migrations/20230420132741_1.2.cs | 48 --- .../Migrations/20230918130909_temp.cs | 301 ------------------ ....cs => 20230918185409_Initial.Designer.cs} | 27 +- .../Migrations/20230918185409_Initial.cs | 132 ++++++++ .../ApplicationDbContextModelSnapshot.cs | 23 +- 14 files changed, 173 insertions(+), 1377 deletions(-) delete mode 100644 src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs delete mode 100644 src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs delete mode 100644 src/Infrastructure/Migrations/20230216010517_1.0.cs delete mode 100644 src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs delete mode 100644 src/Infrastructure/Migrations/20230309081719_2.0.cs delete mode 100644 src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs delete mode 100644 src/Infrastructure/Migrations/20230419193237_1.1.cs delete mode 100644 src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs delete mode 100644 src/Infrastructure/Migrations/20230420132741_1.2.cs delete mode 100644 src/Infrastructure/Migrations/20230918130909_temp.cs rename src/Infrastructure/Migrations/{20230918130909_temp.Designer.cs => 20230918185409_Initial.Designer.cs} (92%) create mode 100644 src/Infrastructure/Migrations/20230918185409_Initial.cs diff --git a/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs b/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs deleted file mode 100644 index 77c05bf5..00000000 --- a/src/Infrastructure/Database/Configuration/TempleCostConfiguration.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using PoEGamblingHelper.Domain.Entity; - -namespace PoEGamblingHelper.Infrastructure.Database.Configuration; - -public class TempleCostConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - var options = new JsonSerializerOptions(); - builder.Property(p => p.ChaosValue) - .HasConversion( - v => JsonSerializer.Serialize(v, options), - v => JsonSerializer.Deserialize(v, options)!); - } -} \ No newline at end of file diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index a8b159fd..13d4ca68 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -29,4 +29,7 @@ + + + diff --git a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs b/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs deleted file mode 100644 index 59c1018a..00000000 --- a/src/Infrastructure/Migrations/20230216010517_1.0.Designer.cs +++ /dev/null @@ -1,170 +0,0 @@ -// -using System; -using PoEGamblingHelper.Infrastructure.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using PoEGamblingHelper.Infrastructure.Database; - -#nullable disable - -namespace Infrastructure.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20230216010517_1.0")] - partial class _10 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entity.Currency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Currency"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Icon") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("GemData"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChaosValue") - .HasColumnType("numeric"); - - b.Property("Corrupted") - .HasColumnType("boolean"); - - b.Property("DetailsId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DivineValue") - .HasColumnType("numeric"); - - b.Property("ExaltedValue") - .HasColumnType("numeric"); - - b.Property("GemDataId") - .HasColumnType("uuid"); - - b.Property("GemLevel") - .HasColumnType("integer"); - - b.Property("GemQuality") - .HasColumnType("integer"); - - b.Property("ListingCount") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("GemDataId"); - - b.ToTable("GemTradeData"); - }); - - modelBuilder.Entity("Domain.Entity.League", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("League"); - }); - - modelBuilder.Entity("Domain.Entity.TempleCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ChaosValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TempleCost"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.HasOne("Domain.Entity.Gem.GemData", null) - .WithMany("Gems") - .HasForeignKey("GemDataId"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Navigation("Gems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Infrastructure/Migrations/20230216010517_1.0.cs b/src/Infrastructure/Migrations/20230216010517_1.0.cs deleted file mode 100644 index c0c8622c..00000000 --- a/src/Infrastructure/Migrations/20230216010517_1.0.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Migrations -{ - /// - public partial class _10 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Currency", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - ChaosEquivalent = table.Column(type: "numeric", nullable: false), - Icon = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Currency", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "GemData", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - Icon = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_GemData", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "League", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - StartDate = table.Column(type: "timestamp with time zone", nullable: false), - Version = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_League", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "TempleCost", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - TimeStamp = table.Column(type: "timestamp with time zone", nullable: false), - ChaosValue = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_TempleCost", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "GemTradeData", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - GemLevel = table.Column(type: "integer", nullable: false), - GemQuality = table.Column(type: "integer", nullable: false), - Corrupted = table.Column(type: "boolean", nullable: false), - DetailsId = table.Column(type: "text", nullable: false), - ChaosValue = table.Column(type: "numeric", nullable: false), - ExaltedValue = table.Column(type: "numeric", nullable: false), - DivineValue = table.Column(type: "numeric", nullable: false), - ListingCount = table.Column(type: "integer", nullable: false), - GemDataId = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_GemTradeData", x => x.Id); - table.ForeignKey( - name: "FK_GemTradeData_GemData_GemDataId", - column: x => x.GemDataId, - principalTable: "GemData", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_GemTradeData_GemDataId", - table: "GemTradeData", - column: "GemDataId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Currency"); - - migrationBuilder.DropTable( - name: "GemTradeData"); - - migrationBuilder.DropTable( - name: "League"); - - migrationBuilder.DropTable( - name: "TempleCost"); - - migrationBuilder.DropTable( - name: "GemData"); - } - } -} diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs b/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs deleted file mode 100644 index fb5dd8b9..00000000 --- a/src/Infrastructure/Migrations/20230309081719_2.0.Designer.cs +++ /dev/null @@ -1,229 +0,0 @@ -// -using System; -using PoEGamblingHelper.Infrastructure.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using PoEGamblingHelper.Infrastructure.Database; - -#nullable disable - -namespace Infrastructure.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20230309081719_2.0")] - partial class _20 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entity.Currency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Currency"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Icon") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("GemData"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChaosValue") - .HasColumnType("numeric"); - - b.Property("Corrupted") - .HasColumnType("boolean"); - - b.Property("DetailsId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DivineValue") - .HasColumnType("numeric"); - - b.Property("ExaltedValue") - .HasColumnType("numeric"); - - b.Property("GemDataId") - .HasColumnType("uuid"); - - b.Property("GemLevel") - .HasColumnType("integer"); - - b.Property("GemQuality") - .HasColumnType("integer"); - - b.Property("ListingCount") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("GemDataId"); - - b.ToTable("GemTradeData"); - }); - - modelBuilder.Entity("Domain.Entity.League", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("League"); - }); - - modelBuilder.Entity("Domain.Entity.Stats.CurrencyResult", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("CurrencyResult"); - }); - - modelBuilder.Entity("Domain.Entity.Stats.Result", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CurrencyResultId") - .HasColumnType("text"); - - b.Property("CurrencyValue") - .HasColumnType("numeric"); - - b.Property("GemTradeDataId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("CurrencyResultId"); - - b.HasIndex("GemTradeDataId"); - - b.ToTable("Result"); - }); - - modelBuilder.Entity("Domain.Entity.TempleCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ChaosValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TempleCost"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.HasOne("Domain.Entity.Gem.GemData", null) - .WithMany("Gems") - .HasForeignKey("GemDataId"); - }); - - modelBuilder.Entity("Domain.Entity.Stats.Result", b => - { - b.HasOne("Domain.Entity.Stats.CurrencyResult", "CurrencyResult") - .WithMany() - .HasForeignKey("CurrencyResultId"); - - b.HasOne("Domain.Entity.Gem.GemTradeData", "GemTradeData") - .WithMany() - .HasForeignKey("GemTradeDataId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("CurrencyResult"); - - b.Navigation("GemTradeData"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Navigation("Gems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Infrastructure/Migrations/20230309081719_2.0.cs b/src/Infrastructure/Migrations/20230309081719_2.0.cs deleted file mode 100644 index 8da177c4..00000000 --- a/src/Infrastructure/Migrations/20230309081719_2.0.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Migrations -{ - /// - public partial class _20 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "CurrencyResult", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - ChaosEquivalent = table.Column(type: "numeric", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CurrencyResult", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Result", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - GemTradeDataId = table.Column(type: "bigint", nullable: false), - CurrencyValue = table.Column(type: "numeric", nullable: false), - CurrencyResultId = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Result", x => x.Id); - table.ForeignKey( - name: "FK_Result_CurrencyResult_CurrencyResultId", - column: x => x.CurrencyResultId, - principalTable: "CurrencyResult", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_Result_GemTradeData_GemTradeDataId", - column: x => x.GemTradeDataId, - principalTable: "GemTradeData", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Result_CurrencyResultId", - table: "Result", - column: "CurrencyResultId"); - - migrationBuilder.CreateIndex( - name: "IX_Result_GemTradeDataId", - table: "Result", - column: "GemTradeDataId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Result"); - - migrationBuilder.DropTable( - name: "CurrencyResult"); - } - } -} diff --git a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs b/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs deleted file mode 100644 index e9029170..00000000 --- a/src/Infrastructure/Migrations/20230419193237_1.1.Designer.cs +++ /dev/null @@ -1,183 +0,0 @@ -// -using System; -using PoEGamblingHelper.Infrastructure.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using PoEGamblingHelper.Infrastructure.Database; - -#nullable disable - -namespace Infrastructure.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20230419193237_1.1")] - partial class _11 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entity.Analytics.View", b => - { - b.Property("IpHash") - .HasColumnType("bytea"); - - b.Property("TimeStamp") - .HasColumnType("date"); - - b.HasKey("IpHash"); - - b.ToTable("View"); - }); - - modelBuilder.Entity("Domain.Entity.Currency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Currency"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Icon") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("GemData"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChaosValue") - .HasColumnType("numeric"); - - b.Property("Corrupted") - .HasColumnType("boolean"); - - b.Property("DetailsId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DivineValue") - .HasColumnType("numeric"); - - b.Property("ExaltedValue") - .HasColumnType("numeric"); - - b.Property("GemDataId") - .HasColumnType("uuid"); - - b.Property("GemLevel") - .HasColumnType("integer"); - - b.Property("GemQuality") - .HasColumnType("integer"); - - b.Property("ListingCount") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("GemDataId"); - - b.ToTable("GemTradeData"); - }); - - modelBuilder.Entity("Domain.Entity.League", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("League"); - }); - - modelBuilder.Entity("Domain.Entity.TempleCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ChaosValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TempleCost"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.HasOne("Domain.Entity.Gem.GemData", null) - .WithMany("Gems") - .HasForeignKey("GemDataId"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Navigation("Gems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Infrastructure/Migrations/20230419193237_1.1.cs b/src/Infrastructure/Migrations/20230419193237_1.1.cs deleted file mode 100644 index 34c122fd..00000000 --- a/src/Infrastructure/Migrations/20230419193237_1.1.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Migrations -{ - /// - public partial class _11 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "View", - columns: table => new - { - IpHash = table.Column(type: "bytea", nullable: false), - TimeStamp = table.Column(type: "date", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_View", x => x.IpHash); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "View"); - } - } -} diff --git a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs b/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs deleted file mode 100644 index 4d370f03..00000000 --- a/src/Infrastructure/Migrations/20230420132741_1.2.Designer.cs +++ /dev/null @@ -1,188 +0,0 @@ -// -using System; -using PoEGamblingHelper.Infrastructure.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using PoEGamblingHelper.Infrastructure.Database; - -#nullable disable - -namespace Infrastructure.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20230420132741_1.2")] - partial class _12 - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entity.Analytics.View", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IpHash") - .IsRequired() - .HasColumnType("bytea"); - - b.Property("TimeStamp") - .HasColumnType("date"); - - b.HasKey("Id"); - - b.ToTable("View"); - }); - - modelBuilder.Entity("Domain.Entity.Currency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b.Property("ChaosEquivalent") - .HasColumnType("numeric"); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Currency"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Icon") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("GemData"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChaosValue") - .HasColumnType("numeric"); - - b.Property("Corrupted") - .HasColumnType("boolean"); - - b.Property("DetailsId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DivineValue") - .HasColumnType("numeric"); - - b.Property("ExaltedValue") - .HasColumnType("numeric"); - - b.Property("GemDataId") - .HasColumnType("uuid"); - - b.Property("GemLevel") - .HasColumnType("integer"); - - b.Property("GemQuality") - .HasColumnType("integer"); - - b.Property("ListingCount") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("GemDataId"); - - b.ToTable("GemTradeData"); - }); - - modelBuilder.Entity("Domain.Entity.League", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("League"); - }); - - modelBuilder.Entity("Domain.Entity.TempleCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ChaosValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TempleCost"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemTradeData", b => - { - b.HasOne("Domain.Entity.Gem.GemData", null) - .WithMany("Gems") - .HasForeignKey("GemDataId"); - }); - - modelBuilder.Entity("Domain.Entity.Gem.GemData", b => - { - b.Navigation("Gems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Infrastructure/Migrations/20230420132741_1.2.cs b/src/Infrastructure/Migrations/20230420132741_1.2.cs deleted file mode 100644 index 65deba8e..00000000 --- a/src/Infrastructure/Migrations/20230420132741_1.2.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Migrations -{ - /// - public partial class _12 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropPrimaryKey( - name: "PK_View", - table: "View"); - - migrationBuilder.AddColumn( - name: "Id", - table: "View", - type: "uuid", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); - - migrationBuilder.AddPrimaryKey( - name: "PK_View", - table: "View", - column: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropPrimaryKey( - name: "PK_View", - table: "View"); - - migrationBuilder.DropColumn( - name: "Id", - table: "View"); - - migrationBuilder.AddPrimaryKey( - name: "PK_View", - table: "View", - column: "IpHash"); - } - } -} diff --git a/src/Infrastructure/Migrations/20230918130909_temp.cs b/src/Infrastructure/Migrations/20230918130909_temp.cs deleted file mode 100644 index 42315612..00000000 --- a/src/Infrastructure/Migrations/20230918130909_temp.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Migrations -{ - /// - public partial class temp : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Currency"); - - migrationBuilder.DropTable( - name: "League"); - - migrationBuilder.DropTable( - name: "Result"); - - migrationBuilder.DropTable( - name: "TempleCost"); - - migrationBuilder.DropTable( - name: "View"); - - migrationBuilder.DropTable( - name: "CurrencyResult"); - - migrationBuilder.DropTable( - name: "GemTradeData"); - - migrationBuilder.DropTable( - name: "GemData"); - - migrationBuilder.CreateTable( - name: "Entity", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - Discriminator = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: true), - ChaosEquivalent = table.Column(type: "numeric", nullable: true), - Icon = table.Column(type: "text", nullable: true), - CurrencyResult_Name = table.Column(type: "text", nullable: true), - CurrencyResult_ChaosEquivalent = table.Column(type: "numeric", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Entity", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Entity", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Discriminator = table.Column(type: "text", nullable: false), - IpHash = table.Column(type: "bytea", nullable: true), - TimeStamp = table.Column(type: "timestamp with time zone", nullable: true), - Name = table.Column(type: "text", nullable: true), - Icon = table.Column(type: "text", nullable: true), - League_Name = table.Column(type: "text", nullable: true), - StartDate = table.Column(type: "timestamp with time zone", nullable: true), - Version = table.Column(type: "text", nullable: true), - GemTradeDataId = table.Column(type: "bigint", nullable: true), - CurrencyValue = table.Column(type: "numeric", nullable: true), - CurrencyResultId = table.Column(type: "text", nullable: true), - TempleCost_TimeStamp = table.Column(type: "timestamp with time zone", nullable: true), - ChaosValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Entity", x => x.Id); - table.ForeignKey( - name: "FK_Entity_Entity_CurrencyResultId", - column: x => x.CurrencyResultId, - principalTable: "Entity", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "Entity", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Discriminator = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: true), - GemLevel = table.Column(type: "integer", nullable: true), - GemQuality = table.Column(type: "integer", nullable: true), - Corrupted = table.Column(type: "boolean", nullable: true), - DetailsId = table.Column(type: "text", nullable: true), - ChaosValue = table.Column(type: "numeric", nullable: true), - ExaltedValue = table.Column(type: "numeric", nullable: true), - DivineValue = table.Column(type: "numeric", nullable: true), - ListingCount = table.Column(type: "integer", nullable: true), - GemDataId = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Entity", x => x.Id); - table.ForeignKey( - name: "FK_Entity_Entity_GemDataId", - column: x => x.GemDataId, - principalTable: "Entity", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Entity_CurrencyResultId", - table: "Entity", - column: "CurrencyResultId"); - - migrationBuilder.CreateIndex( - name: "IX_Entity_GemTradeDataId", - table: "Entity", - column: "GemTradeDataId"); - - migrationBuilder.CreateIndex( - name: "IX_Entity_GemDataId", - table: "Entity", - column: "GemDataId"); - - migrationBuilder.AddForeignKey( - name: "FK_Entity_Entity_GemTradeDataId", - table: "Entity", - column: "GemTradeDataId", - principalTable: "Entity", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Entity_Entity_GemTradeDataId", - table: "Entity"); - - migrationBuilder.DropTable( - name: "Entity"); - - migrationBuilder.DropTable( - name: "Entity"); - - migrationBuilder.DropTable( - name: "Entity"); - - migrationBuilder.CreateTable( - name: "Currency", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ChaosEquivalent = table.Column(type: "numeric", nullable: false), - Icon = table.Column(type: "text", nullable: true), - Name = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Currency", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "CurrencyResult", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ChaosEquivalent = table.Column(type: "numeric", nullable: false), - Name = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CurrencyResult", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "GemData", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Icon = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_GemData", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "League", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - StartDate = table.Column(type: "timestamp with time zone", nullable: false), - Version = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_League", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "TempleCost", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ChaosValue = table.Column(type: "text", nullable: false), - TimeStamp = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_TempleCost", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "View", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IpHash = table.Column(type: "bytea", nullable: false), - TimeStamp = table.Column(type: "date", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_View", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "GemTradeData", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ChaosValue = table.Column(type: "numeric", nullable: false), - Corrupted = table.Column(type: "boolean", nullable: false), - DetailsId = table.Column(type: "text", nullable: false), - DivineValue = table.Column(type: "numeric", nullable: false), - ExaltedValue = table.Column(type: "numeric", nullable: false), - GemDataId = table.Column(type: "uuid", nullable: true), - GemLevel = table.Column(type: "integer", nullable: false), - GemQuality = table.Column(type: "integer", nullable: false), - ListingCount = table.Column(type: "integer", nullable: false), - Name = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_GemTradeData", x => x.Id); - table.ForeignKey( - name: "FK_GemTradeData_GemData_GemDataId", - column: x => x.GemDataId, - principalTable: "GemData", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "Result", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - CurrencyResultId = table.Column(type: "text", nullable: true), - GemTradeDataId = table.Column(type: "bigint", nullable: false), - CurrencyValue = table.Column(type: "numeric", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Result", x => x.Id); - table.ForeignKey( - name: "FK_Result_CurrencyResult_CurrencyResultId", - column: x => x.CurrencyResultId, - principalTable: "CurrencyResult", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_Result_GemTradeData_GemTradeDataId", - column: x => x.GemTradeDataId, - principalTable: "GemTradeData", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_GemTradeData_GemDataId", - table: "GemTradeData", - column: "GemDataId"); - - migrationBuilder.CreateIndex( - name: "IX_Result_CurrencyResultId", - table: "Result", - column: "CurrencyResultId"); - - migrationBuilder.CreateIndex( - name: "IX_Result_GemTradeDataId", - table: "Result", - column: "GemTradeDataId"); - } - } -} diff --git a/src/Infrastructure/Migrations/20230918130909_temp.Designer.cs b/src/Infrastructure/Migrations/20230918185409_Initial.Designer.cs similarity index 92% rename from src/Infrastructure/Migrations/20230918130909_temp.Designer.cs rename to src/Infrastructure/Migrations/20230918185409_Initial.Designer.cs index db9d3a14..a95c9098 100644 --- a/src/Infrastructure/Migrations/20230918130909_temp.Designer.cs +++ b/src/Infrastructure/Migrations/20230918185409_Initial.Designer.cs @@ -9,11 +9,11 @@ #nullable disable -namespace Infrastructure.Migrations +namespace PoEGamblingHelper.Infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20230918130909_temp")] - partial class temp + [Migration("20230918185409_Initial")] + partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -84,6 +84,19 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.UseTphMappingStrategy(); }); + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.AnalyticsDay", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("Views") + .HasColumnType("bigint"); + + b.HasDiscriminator().HasValue("AnalyticsDay"); + }); + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.View", b => { b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); @@ -92,8 +105,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("bytea"); - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); + b.Property("TimeStamp") + .HasColumnType("date"); b.HasDiscriminator().HasValue("View"); }); @@ -161,9 +174,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); - b.Property("ChaosValue") + b.Property("ChaosValue") .IsRequired() - .HasColumnType("text"); + .HasColumnType("numeric[]"); b.Property("TimeStamp") .HasColumnType("timestamp with time zone"); diff --git a/src/Infrastructure/Migrations/20230918185409_Initial.cs b/src/Infrastructure/Migrations/20230918185409_Initial.cs new file mode 100644 index 00000000..c7863685 --- /dev/null +++ b/src/Infrastructure/Migrations/20230918185409_Initial.cs @@ -0,0 +1,132 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PoEGamblingHelper.Infrastructure.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Discriminator = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + ChaosEquivalent = table.Column(type: "numeric", nullable: true), + Icon = table.Column(type: "text", nullable: true), + CurrencyResult_Name = table.Column(type: "text", nullable: true), + CurrencyResult_ChaosEquivalent = table.Column(type: "numeric", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Discriminator = table.Column(type: "text", nullable: false), + Date = table.Column(type: "date", nullable: true), + Views = table.Column(type: "bigint", nullable: true), + IpHash = table.Column(type: "bytea", nullable: true), + TimeStamp = table.Column(type: "date", nullable: true), + Name = table.Column(type: "text", nullable: true), + Icon = table.Column(type: "text", nullable: true), + League_Name = table.Column(type: "text", nullable: true), + StartDate = table.Column(type: "timestamp with time zone", nullable: true), + Version = table.Column(type: "text", nullable: true), + GemTradeDataId = table.Column(type: "bigint", nullable: true), + CurrencyValue = table.Column(type: "numeric", nullable: true), + CurrencyResultId = table.Column(type: "text", nullable: true), + TempleCost_TimeStamp = table.Column(type: "timestamp with time zone", nullable: true), + ChaosValue = table.Column(type: "numeric[]", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + table.ForeignKey( + name: "FK_Entity_Entity_CurrencyResultId", + column: x => x.CurrencyResultId, + principalTable: "Entity", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Entity", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Discriminator = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + GemLevel = table.Column(type: "integer", nullable: true), + GemQuality = table.Column(type: "integer", nullable: true), + Corrupted = table.Column(type: "boolean", nullable: true), + DetailsId = table.Column(type: "text", nullable: true), + ChaosValue = table.Column(type: "numeric", nullable: true), + ExaltedValue = table.Column(type: "numeric", nullable: true), + DivineValue = table.Column(type: "numeric", nullable: true), + ListingCount = table.Column(type: "integer", nullable: true), + GemDataId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Entity", x => x.Id); + table.ForeignKey( + name: "FK_Entity_Entity_GemDataId", + column: x => x.GemDataId, + principalTable: "Entity", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Entity_CurrencyResultId", + table: "Entity", + column: "CurrencyResultId"); + + migrationBuilder.CreateIndex( + name: "IX_Entity_GemTradeDataId", + table: "Entity", + column: "GemTradeDataId"); + + migrationBuilder.CreateIndex( + name: "IX_Entity_GemDataId", + table: "Entity", + column: "GemDataId"); + + migrationBuilder.AddForeignKey( + name: "FK_Entity_Entity_GemTradeDataId", + table: "Entity", + column: "GemTradeDataId", + principalTable: "Entity", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Entity_Entity_GemTradeDataId", + table: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + + migrationBuilder.DropTable( + name: "Entity"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 7ccc8458..ad5e900d 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -8,7 +8,7 @@ #nullable disable -namespace Infrastructure.Migrations +namespace PoEGamblingHelper.Infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] partial class ApplicationDbContextModelSnapshot : ModelSnapshot @@ -81,6 +81,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.UseTphMappingStrategy(); }); + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.AnalyticsDay", b => + { + b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("Views") + .HasColumnType("bigint"); + + b.HasDiscriminator().HasValue("AnalyticsDay"); + }); + modelBuilder.Entity("PoEGamblingHelper.Domain.Entity.Analytics.View", b => { b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); @@ -89,8 +102,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("bytea"); - b.Property("TimeStamp") - .HasColumnType("timestamp with time zone"); + b.Property("TimeStamp") + .HasColumnType("date"); b.HasDiscriminator().HasValue("View"); }); @@ -158,9 +171,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasBaseType("PoEGamblingHelper.Domain.Entity.Abstract.Entity"); - b.Property("ChaosValue") + b.Property("ChaosValue") .IsRequired() - .HasColumnType("text"); + .HasColumnType("numeric[]"); b.Property("TimeStamp") .HasColumnType("timestamp with time zone"); From 63b71a5087210276de4fa9bd9f39b2a08a6f8deb Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 22:06:38 +0200 Subject: [PATCH 047/129] cleanup --- .../Database/ApplicationDbContext.cs | 17 ++--- .../Exceptions/DbConcurrencyException.cs | 8 -- .../Repositories/LeagueRepository.cs | 3 +- .../Repositories/ViewRepository.cs | 2 +- .../Services/DateTimeService.cs | 4 +- src/Infrastructure/Services/FetchDto.cs | 73 +++++++++++++++++++ .../Services/FetchDtos/CurrencyPriceData.cs | 8 -- .../Services/FetchDtos/GemPriceData.cs | 7 -- .../FetchDtos/PoeNinjaCurrencyData.cs | 23 ------ .../FetchDtos/PoeNinjaCurrencyDetails.cs | 7 -- .../Services/FetchDtos/PoeNinjaGemData.cs | 35 --------- .../Services/FetchDtos/TradeEntry.cs | 8 -- .../Services/FetchDtos/TradeEntryListing.cs | 6 -- .../FetchDtos/TradeEntryListingPrice.cs | 19 ----- .../Services/FetchDtos/TradeEntryResult.cs | 6 -- .../Services/FetchDtos/TradeResults.cs | 9 --- src/Infrastructure/Services/HashingService.cs | 4 +- src/Infrastructure/Services/IDataFetcher.cs | 2 +- 18 files changed, 91 insertions(+), 150 deletions(-) delete mode 100644 src/Infrastructure/Exceptions/DbConcurrencyException.cs create mode 100644 src/Infrastructure/Services/FetchDto.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/GemPriceData.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/TradeEntry.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs delete mode 100644 src/Infrastructure/Services/FetchDtos/TradeResults.cs diff --git a/src/Infrastructure/Database/ApplicationDbContext.cs b/src/Infrastructure/Database/ApplicationDbContext.cs index 98b4c815..b2b1091a 100644 --- a/src/Infrastructure/Database/ApplicationDbContext.cs +++ b/src/Infrastructure/Database/ApplicationDbContext.cs @@ -9,16 +9,15 @@ namespace PoEGamblingHelper.Infrastructure.Database; public class ApplicationDbContext : DbContext { + public readonly DbSet AnalyticsDay = null!; + public readonly DbSet Currency = null!; + public readonly DbSet GemData = null!; + public readonly DbSet GemTradeData = null!; + public readonly DbSet League = null!; + public readonly DbSet Result = null!; + public readonly DbSet TempleCost = null!; + public readonly DbSet View = null!; public ApplicationDbContext(DbContextOptions options) : base(options) { } - public virtual DbSet Currency => Set(); - public virtual DbSet League => Set(); - public virtual DbSet TempleCost => Set(); - public virtual DbSet GemData => Set(); - public virtual DbSet GemTradeData => Set(); - public virtual DbSet Result => Set(); - public virtual DbSet AnalyticsDay => Set(); - public virtual DbSet View => Set(); - public Task SaveChangesAsync() { return base.SaveChangesAsync(); } public void ClearTrackedEntities() { ChangeTracker.Clear(); } diff --git a/src/Infrastructure/Exceptions/DbConcurrencyException.cs b/src/Infrastructure/Exceptions/DbConcurrencyException.cs deleted file mode 100644 index e7b99794..00000000 --- a/src/Infrastructure/Exceptions/DbConcurrencyException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using PoEGamblingHelper.Application.Exception.Abstract; - -namespace PoEGamblingHelper.Infrastructure.Exceptions; - -public class DbConcurrencyException : PoeGamblingHelperException -{ - public DbConcurrencyException(string? message) : base(message) { } -} \ No newline at end of file diff --git a/src/Infrastructure/Repositories/LeagueRepository.cs b/src/Infrastructure/Repositories/LeagueRepository.cs index e3d9a3df..0b48d5fa 100644 --- a/src/Infrastructure/Repositories/LeagueRepository.cs +++ b/src/Infrastructure/Repositories/LeagueRepository.cs @@ -23,7 +23,8 @@ public LeagueRepository(IDbContextFactory dbContextFactory public League GetByStartDateBefore(DateTime dateTime) { using var applicationDbContext = _dbContextFactory.CreateDbContext(); - return applicationDbContext.League.Where(league => league.StartDate <= dateTime) + return applicationDbContext.League + .Where(league => league.StartDate <= dateTime) .OrderByDescending(league => league.StartDate) .FirstOrDefault() ?? throw new NoLeagueDataException(); diff --git a/src/Infrastructure/Repositories/ViewRepository.cs b/src/Infrastructure/Repositories/ViewRepository.cs index 8e052892..72a61305 100644 --- a/src/Infrastructure/Repositories/ViewRepository.cs +++ b/src/Infrastructure/Repositories/ViewRepository.cs @@ -23,7 +23,7 @@ public ViewRepository(IDbContextFactory dbContextFactory, public async Task AddAsync(string ipAddress) { - var view = new View() + var view = new View { IpHash = _hashingService.HashIpAddress(ipAddress), TimeStamp = _dateTimeService.UtcToday() diff --git a/src/Infrastructure/Services/DateTimeService.cs b/src/Infrastructure/Services/DateTimeService.cs index 18a4ee0c..26c94374 100644 --- a/src/Infrastructure/Services/DateTimeService.cs +++ b/src/Infrastructure/Services/DateTimeService.cs @@ -1,7 +1,9 @@ -using PoEGamblingHelper.Application.Services; +using System.Diagnostics.CodeAnalysis; +using PoEGamblingHelper.Application.Services; namespace PoEGamblingHelper.Infrastructure.Services; +[SuppressMessage("Performance", "CA1822:Member als statisch markieren")] public class DateTimeService : IDateTimeService { public DateOnly UtcToday() { return DateOnly.FromDateTime(UtcNow()); } diff --git a/src/Infrastructure/Services/FetchDto.cs b/src/Infrastructure/Services/FetchDto.cs new file mode 100644 index 00000000..f40e301a --- /dev/null +++ b/src/Infrastructure/Services/FetchDto.cs @@ -0,0 +1,73 @@ +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Gem; + +namespace PoEGamblingHelper.Infrastructure.Services; + +public record CurrencyPriceData(PoeNinjaCurrencyData[] Lines, PoeNinjaCurrencyDetails[] CurrencyDetails); + +public record GemPriceData(PoeNinjaGemData[] Lines); + +public record PoeNinjaCurrencyData(string CurrencyTypeName, decimal ChaosEquivalent, string DetailsId, string? Icon) +{ + public Currency ToCurrencyData() + { + return new Currency + { + Id = DetailsId, + Name = CurrencyTypeName, + ChaosEquivalent = ChaosEquivalent, + Icon = Icon + }; + } +} + +public record PoeNinjaCurrencyDetails(string Name, string? Icon); + +public record PoeNinjaGemData(long Id, + string Name, + string Icon, + int GemLevel, + int GemQuality, + bool Corrupted, + string DetailsId, + decimal ChaosValue, + decimal ExaltedValue, + decimal DivineValue, + int ListingCount) +{ + public GemTradeData ToGemTradeData() + { + return new GemTradeData + { + Id = Id, + Name = Name, + GemLevel = GemLevel, + GemQuality = GemQuality, + Corrupted = Corrupted, + DetailsId = DetailsId, + ChaosValue = ChaosValue, + ExaltedValue = ExaltedValue, + DivineValue = DivineValue, + ListingCount = ListingCount + }; + } +} + +public record TradeEntry(string Id, TradeEntryListing Listing); + +public record TradeEntryListing(TradeEntryListingPrice Price); + +public record TradeEntryListingPrice(string Type, decimal Amount, string Currency) +{ + public decimal ChaosAmount(IEnumerable currencySet) //TODO + { + var lowerCurrencyName = (Currency + " orb").ToLower(); + var currency = currencySet.FirstOrDefault(c => c.Name.ToLower().Equals(lowerCurrencyName)); + var conversionValue = currency?.ChaosEquivalent ?? 1; + return Amount * conversionValue; + } +} + +public record TradeEntryResult(TradeEntry[] Result); + +public record TradeResults(string Id, int Complexity, string[] Result, int Total); \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs b/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs deleted file mode 100644 index 96ea5146..00000000 --- a/src/Infrastructure/Services/FetchDtos/CurrencyPriceData.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class CurrencyPriceData -{ - public PoeNinjaCurrencyData[] Lines { get; set; } = null!; - public PoeNinjaCurrencyDetails[] CurrencyDetails { get; set; } = null!; - public override string ToString() { return string.Join(", ", Lines.AsEnumerable()); } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/GemPriceData.cs b/src/Infrastructure/Services/FetchDtos/GemPriceData.cs deleted file mode 100644 index 386009b3..00000000 --- a/src/Infrastructure/Services/FetchDtos/GemPriceData.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class GemPriceData -{ - public PoeNinjaGemData[] Lines { get; set; } = null!; - public override string ToString() { return string.Join(", ", Lines.AsEnumerable()); } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs deleted file mode 100644 index 774ab7cf..00000000 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyData.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; -using PoEGamblingHelper.Domain.Entity; - -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class PoeNinjaCurrencyData -{ - [JsonPropertyName("currencyTypeName")] public string Name { get; set; } = null!; - public decimal ChaosEquivalent { get; set; } - public string DetailsId { get; set; } = null!; - public string? Icon { get; set; } - - public Currency ToCurrencyData() - { - return new Currency - { - Id = DetailsId, - Name = Name, - ChaosEquivalent = ChaosEquivalent, - Icon = Icon - }; - } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs deleted file mode 100644 index c197e52f..00000000 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaCurrencyDetails.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class PoeNinjaCurrencyDetails -{ - public string Name { get; set; } = null!; - public string? Icon { get; set; } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs b/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs deleted file mode 100644 index df0b73ea..00000000 --- a/src/Infrastructure/Services/FetchDtos/PoeNinjaGemData.cs +++ /dev/null @@ -1,35 +0,0 @@ -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class PoeNinjaGemData -{ - public long Id { get; set; } - public string Name { get; set; } = null!; - public string Icon { get; set; } = null!; - public int GemLevel { get; set; } - public int GemQuality { get; set; } - public bool Corrupted { get; set; } - public string DetailsId { get; set; } = null!; - public decimal ChaosValue { get; set; } - public decimal ExaltedValue { get; set; } - public decimal DivineValue { get; set; } - public int ListingCount { get; set; } - - public GemTradeData ToGemTradeData() - { - return new GemTradeData - { - Id = Id, - Name = Name, - GemLevel = GemLevel, - GemQuality = GemQuality, - Corrupted = Corrupted, - DetailsId = DetailsId, - ChaosValue = ChaosValue, - ExaltedValue = ExaltedValue, - DivineValue = DivineValue, - ListingCount = ListingCount - }; - } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntry.cs b/src/Infrastructure/Services/FetchDtos/TradeEntry.cs deleted file mode 100644 index c3b08b0e..00000000 --- a/src/Infrastructure/Services/FetchDtos/TradeEntry.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class TradeEntry -{ - public string Id { get; set; } = null!; - - public TradeEntryListing Listing { get; set; } = null!; -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs deleted file mode 100644 index 75dec861..00000000 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryListing.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class TradeEntryListing -{ - public TradeEntryListingPrice Price { get; set; } = null!; -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs deleted file mode 100644 index 7171eba6..00000000 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryListingPrice.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using PoEGamblingHelper.Domain.Entity; - -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class TradeEntryListingPrice -{ - public string Type { get; set; } = null!; - public decimal Amount { get; set; } - public string Currency { get; set; } = null!; - - public decimal ChaosAmount(DbSet currencySet) - { - var lowerCurrencyName = (Currency + " orb").ToLower(); - var currency = currencySet.FirstOrDefault(c => c.Name.ToLower().Equals(lowerCurrencyName)); - var conversionValue = currency?.ChaosEquivalent ?? 1; - return Amount * conversionValue; - } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs b/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs deleted file mode 100644 index 5f8b82a6..00000000 --- a/src/Infrastructure/Services/FetchDtos/TradeEntryResult.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class TradeEntryResult -{ - public TradeEntry[] Result { get; set; } = null!; -} \ No newline at end of file diff --git a/src/Infrastructure/Services/FetchDtos/TradeResults.cs b/src/Infrastructure/Services/FetchDtos/TradeResults.cs deleted file mode 100644 index 412988a8..00000000 --- a/src/Infrastructure/Services/FetchDtos/TradeResults.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -public class TradeResults -{ - public string Id { get; set; } = null!; - public int Complexity { get; set; } - public string[] Result { get; set; } = null!; - public int Total { get; set; } -} \ No newline at end of file diff --git a/src/Infrastructure/Services/HashingService.cs b/src/Infrastructure/Services/HashingService.cs index 4793ecbd..ca3f0f78 100644 --- a/src/Infrastructure/Services/HashingService.cs +++ b/src/Infrastructure/Services/HashingService.cs @@ -1,9 +1,11 @@ -using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; using System.Text; using PoEGamblingHelper.Application.Services; namespace PoEGamblingHelper.Infrastructure.Services; +[SuppressMessage("Performance", "CA1822:Member als statisch markieren")] public class HashingService : IHashingService { public byte[] HashIpAddress(string ipAddress) { return SHA512.HashData(Encoding.UTF8.GetBytes(ipAddress)); } diff --git a/src/Infrastructure/Services/IDataFetcher.cs b/src/Infrastructure/Services/IDataFetcher.cs index 9cf51e0d..529d618b 100644 --- a/src/Infrastructure/Services/IDataFetcher.cs +++ b/src/Infrastructure/Services/IDataFetcher.cs @@ -2,7 +2,7 @@ namespace PoEGamblingHelper.Infrastructure.Services; -public interface IDataFetcher //TODO rename +public interface IDataFetcher { Task FetchCurrentLeague(); Task FetchCurrencyData(League league); From 4d10f47143b2961befa8e00cae27d22b189d3ef4 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 22:12:04 +0200 Subject: [PATCH 048/129] cleanup --- .../Extensions/GemDataExtensions.cs | 12 ++++----- .../BackgroundJobs/BackgroundJob.cs | 2 +- .../BackgroundJobs/FetchLeagueJob.cs | 2 +- .../BackgroundJobs/FetchPriceDataJob.cs | 2 +- .../{Services => DataFetcher}/DataFetcher.cs | 26 +++++++++---------- .../{Services => DataFetcher}/FetchDto.cs | 9 +++++-- .../{Services => DataFetcher}/IDataFetcher.cs | 2 +- .../{Util => DataFetcher}/PoeToolUrls.cs | 2 +- .../GemDataExtensions.cs} | 6 ++--- .../ServiceCollectionExtensions.cs | 3 ++- .../Services/InitServiceTest.cs | 1 - .../Services/LeagueServiceTest.cs | 4 +-- .../FetchDtos/PoeNinjaCurrencyDataTest.cs | 5 +--- .../Services/FetchDtos/PoeNinjaGemDataTest.cs | 5 +--- .../FetchDtos/TradeEntryListingPriceTest.cs | 4 +-- .../Util/ExtensionMethodsTest.cs | 5 +--- 16 files changed, 41 insertions(+), 49 deletions(-) rename src/Infrastructure/{Services => DataFetcher}/DataFetcher.cs (94%) rename src/Infrastructure/{Services => DataFetcher}/FetchDto.cs (89%) rename src/Infrastructure/{Services => DataFetcher}/IDataFetcher.cs (81%) rename src/Infrastructure/{Util => DataFetcher}/PoeToolUrls.cs (90%) rename src/Infrastructure/{Util/ExtensionMethods.cs => Extensions/GemDataExtensions.cs} (86%) diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index 0b84448a..4161603d 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -37,8 +37,8 @@ private static decimal Profit(this GemData gemData, } /// - /// Finds the ChaosValue of the Gem for the specified resultCase. - /// If there are multiple, it just gets the cheapest one. + /// Finds the ChaosValue of the Gem for the specified resultCase. + /// If there are multiple, it just gets the cheapest one. /// private static decimal Value(this GemData gemData, ResultCase resultCase) { @@ -46,8 +46,8 @@ private static decimal Value(this GemData gemData, ResultCase resultCase) } /// - /// Finds the ChaosValue of the Gem with the specified level and corrupted. - /// If there are multiple, it just gets the cheapest one. + /// Finds the ChaosValue of the Gem with the specified level and corrupted. + /// If there are multiple, it just gets the cheapest one. /// private static decimal ResultValue(this GemData gemData, int level) { @@ -76,8 +76,8 @@ private static decimal CostPerTry(this GemData gemData, decimal? rawCost = null, } /// - /// Finds the ChaosValue of the Gem with maximum level and not corrupted. - /// If there are multiple, it just gets the cheapest one. + /// Finds the ChaosValue of the Gem with maximum level and not corrupted. + /// If there are multiple, it just gets the cheapest one. /// public static decimal RawCost(this GemData gemData) { diff --git a/src/Infrastructure/BackgroundJobs/BackgroundJob.cs b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs index 115fc7db..3572b893 100644 --- a/src/Infrastructure/BackgroundJobs/BackgroundJob.cs +++ b/src/Infrastructure/BackgroundJobs/BackgroundJob.cs @@ -10,7 +10,7 @@ public abstract class BackgroundJob : BackgroundService private readonly TimeSpan _interval; /// - /// Gets the StartTime from the configuration as well. + /// Gets the StartTime from the configuration as well. /// protected BackgroundJob(IConfiguration configuration, IDateTimeService dateTimeService) : this(configuration) { diff --git a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs index fa9a1d98..b82ed108 100644 --- a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception.Abstract; -using PoEGamblingHelper.Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.DataFetcher; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index ff0ef766..c63dcd35 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -5,7 +5,7 @@ using PoEGamblingHelper.Application.Exception.Abstract; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Infrastructure.Services; +using PoEGamblingHelper.Infrastructure.DataFetcher; namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; diff --git a/src/Infrastructure/Services/DataFetcher.cs b/src/Infrastructure/DataFetcher/DataFetcher.cs similarity index 94% rename from src/Infrastructure/Services/DataFetcher.cs rename to src/Infrastructure/DataFetcher/DataFetcher.cs index e0af7ec7..2125423f 100644 --- a/src/Infrastructure/Services/DataFetcher.cs +++ b/src/Infrastructure/DataFetcher/DataFetcher.cs @@ -9,10 +9,9 @@ using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Infrastructure.Database; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; -using PoEGamblingHelper.Infrastructure.Util; +using PoEGamblingHelper.Infrastructure.Extensions; -namespace PoEGamblingHelper.Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.DataFetcher; public partial class DataFetcher : IDataFetcher { @@ -79,11 +78,12 @@ public async Task FetchCurrencyData(League league) var currencyDetails = currencyPriceData.CurrencyDetails .FirstOrDefault(currencyDetails => - currencyData.Name.EqualsIgnoreCase(currencyDetails.Name)); + currencyData.CurrencyTypeName.EqualsIgnoreCase( + currencyDetails.Name)); if (currencyDetails is not null) currencyData.Icon = currencyDetails.Icon; } - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); var existingCurrency = applicationDbContext.Currency.Select(currency => currency.Id).ToArray(); applicationDbContext.ClearTrackedEntities(); @@ -132,9 +132,9 @@ public async Task FetchTemplePriceData(League league) TradeResults temples; var requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/search/{league.Name}"; - using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri) - { Content = new StringContent(_templeQuery, _jsonMediaTypeHeader) }) + using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri)) { + request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); var result = await SendAsync(request); if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); @@ -156,9 +156,9 @@ public async Task FetchTemplePriceData(League league) TradeEntryResult priceResults; requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/fetch/{itemQuery}?query={temples.Id}"; - using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri) - { Content = new StringContent(_templeQuery, _jsonMediaTypeHeader) }) + using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) { + request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); var result = await SendAsync(request); if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); @@ -170,7 +170,7 @@ public async Task FetchTemplePriceData(League league) #endregion - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); var chaosValues = priceResults.Result .Select(priceResult => priceResult.Listing @@ -195,7 +195,7 @@ public async Task FetchGemPriceData(League league) _logger.LogInformation("Got data from {Result} gems", gemPriceData.Lines.Length); // GemTradeData - using (var applicationDbContext = _applicationDbContextFactory.CreateDbContext()) + await using (var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync()) { var existingGemTradeData = applicationDbContext.GemTradeData.Select(gemTradeData => gemTradeData.Id).ToArray(); @@ -214,7 +214,7 @@ await applicationDbContext.GemTradeData.AddRangeAsync( } // GemData - using (var applicationDbContext = _applicationDbContextFactory.CreateDbContext()) + await using (var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync()) { var existingGemData = applicationDbContext.GemData.ToArray(); var existingGemDataNames = existingGemData.Select(gem => gem.Name.ToLowerInvariant().Trim()).ToArray(); @@ -270,7 +270,7 @@ private async Task ParseLeagueRows(IEnumerable leagueRows, var fullDateRegex = FullDateRegex(); var nameExpansionRegex = NameExpansionRegex(); - using var applicationDbContext = _applicationDbContextFactory.CreateDbContext(); + await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); foreach (var row in leagueRows) { var releaseDateText = row.ChildNodes[releaseColumnIndex].InnerText; diff --git a/src/Infrastructure/Services/FetchDto.cs b/src/Infrastructure/DataFetcher/FetchDto.cs similarity index 89% rename from src/Infrastructure/Services/FetchDto.cs rename to src/Infrastructure/DataFetcher/FetchDto.cs index f40e301a..57580b6a 100644 --- a/src/Infrastructure/Services/FetchDto.cs +++ b/src/Infrastructure/DataFetcher/FetchDto.cs @@ -1,14 +1,19 @@ using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; -namespace PoEGamblingHelper.Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.DataFetcher; public record CurrencyPriceData(PoeNinjaCurrencyData[] Lines, PoeNinjaCurrencyDetails[] CurrencyDetails); public record GemPriceData(PoeNinjaGemData[] Lines); -public record PoeNinjaCurrencyData(string CurrencyTypeName, decimal ChaosEquivalent, string DetailsId, string? Icon) +public class PoeNinjaCurrencyData { + public string CurrencyTypeName { get; init; } + public decimal ChaosEquivalent { get; init; } + public string DetailsId { get; init; } + public string? Icon { get; set; } + public Currency ToCurrencyData() { return new Currency diff --git a/src/Infrastructure/Services/IDataFetcher.cs b/src/Infrastructure/DataFetcher/IDataFetcher.cs similarity index 81% rename from src/Infrastructure/Services/IDataFetcher.cs rename to src/Infrastructure/DataFetcher/IDataFetcher.cs index 529d618b..08a8112f 100644 --- a/src/Infrastructure/Services/IDataFetcher.cs +++ b/src/Infrastructure/DataFetcher/IDataFetcher.cs @@ -1,6 +1,6 @@ using PoEGamblingHelper.Domain.Entity; -namespace PoEGamblingHelper.Infrastructure.Services; +namespace PoEGamblingHelper.Infrastructure.DataFetcher; public interface IDataFetcher { diff --git a/src/Infrastructure/Util/PoeToolUrls.cs b/src/Infrastructure/DataFetcher/PoeToolUrls.cs similarity index 90% rename from src/Infrastructure/Util/PoeToolUrls.cs rename to src/Infrastructure/DataFetcher/PoeToolUrls.cs index 889d52b9..d2928ec0 100644 --- a/src/Infrastructure/Util/PoeToolUrls.cs +++ b/src/Infrastructure/DataFetcher/PoeToolUrls.cs @@ -1,4 +1,4 @@ -namespace PoEGamblingHelper.Infrastructure.Util; +namespace PoEGamblingHelper.Infrastructure.DataFetcher; public static class PoeToolUrls { diff --git a/src/Infrastructure/Util/ExtensionMethods.cs b/src/Infrastructure/Extensions/GemDataExtensions.cs similarity index 86% rename from src/Infrastructure/Util/ExtensionMethods.cs rename to src/Infrastructure/Extensions/GemDataExtensions.cs index bc38eebf..f15584b2 100644 --- a/src/Infrastructure/Util/ExtensionMethods.cs +++ b/src/Infrastructure/Extensions/GemDataExtensions.cs @@ -1,10 +1,10 @@ using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; +using PoEGamblingHelper.Infrastructure.DataFetcher; -namespace PoEGamblingHelper.Infrastructure.Util; +namespace PoEGamblingHelper.Infrastructure.Extensions; -public static class ExtensionMethods +public static class GemDataExtensions { public static GemData ToGemData(this IGrouping group, IEnumerable gemTradeData, diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index f9f449ef..694d3c82 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -5,6 +5,7 @@ using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Infrastructure.BackgroundJobs; using PoEGamblingHelper.Infrastructure.Database; +using PoEGamblingHelper.Infrastructure.DataFetcher; using PoEGamblingHelper.Infrastructure.Repositories; using PoEGamblingHelper.Infrastructure.Services; @@ -25,7 +26,7 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/test/Application.Test/Services/InitServiceTest.cs b/test/Application.Test/Services/InitServiceTest.cs index 2fcadb30..313ef98d 100644 --- a/test/Application.Test/Services/InitServiceTest.cs +++ b/test/Application.Test/Services/InitServiceTest.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Logging; using Moq; using PoEGamblingHelper.Application.Exception; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; #pragma warning disable CS8625 diff --git a/test/Application.Test/Services/LeagueServiceTest.cs b/test/Application.Test/Services/LeagueServiceTest.cs index ca374519..6803b5e1 100644 --- a/test/Application.Test/Services/LeagueServiceTest.cs +++ b/test/Application.Test/Services/LeagueServiceTest.cs @@ -1,7 +1,5 @@ -using FluentAssertions; -using MockQueryable.Moq; +using MockQueryable.Moq; using PoEGamblingHelper.Application.Exception; -using PoEGamblingHelper.Application.Services; using PoEGamblingHelper.Domain.Entity; namespace PoEGamblingHelper.Application.Test.Services; diff --git a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs index efba904c..4d27a8c6 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaCurrencyDataTest.cs @@ -1,7 +1,4 @@ -using FluentAssertions; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -namespace Infrastructure.Test.Services.FetchDtos; +namespace Infrastructure.Test.Services.FetchDtos; public class PoeNinjaCurrencyDataTest { diff --git a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs index 70714e53..69d582d9 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/PoeNinjaGemDataTest.cs @@ -1,7 +1,4 @@ -using FluentAssertions; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; - -namespace Infrastructure.Test.Services.FetchDtos; +namespace Infrastructure.Test.Services.FetchDtos; public class PoeNinjaGemDataTest { diff --git a/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs b/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs index 30bb4177..0c6f3646 100644 --- a/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs +++ b/test/Infrastructure.Test/Services/FetchDtos/TradeEntryListingPriceTest.cs @@ -1,7 +1,5 @@ -using FluentAssertions; -using MockQueryable.Moq; +using MockQueryable.Moq; using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; namespace Infrastructure.Test.Services.FetchDtos; diff --git a/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs b/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs index 805d6b37..815c69b9 100644 --- a/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs +++ b/test/Infrastructure.Test/Util/ExtensionMethodsTest.cs @@ -1,7 +1,4 @@ -using FluentAssertions; -using PoEGamblingHelper.Domain.Entity.Gem; -using PoEGamblingHelper.Infrastructure.Services.FetchDtos; -using PoEGamblingHelper.Infrastructure.Util; +using PoEGamblingHelper.Domain.Entity.Gem; namespace Infrastructure.Test.Util; From 5db102b0e3688af5e8a1229c83352c0ab8929b54 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 22:57:47 +0200 Subject: [PATCH 049/129] cleanup Infrastructure & update to DotNet 8 --- Directory.Build.props | 1 + src/Api/Api.csproj | 4 +- src/Api/Controllers/ApiControllerBase.cs | 6 +- src/Api/Controllers/CurrencyController.cs | 11 +- src/Api/Controllers/GemController.cs | 11 +- src/Api/Controllers/LeagueController.cs | 13 +- src/Api/Controllers/TempleController.cs | 5 +- src/Application/Application.csproj | 2 +- src/Domain/Domain.csproj | 2 +- .../BackgroundJobs/FetchLeagueJob.cs | 20 +- .../BackgroundJobs/FetchPriceDataJob.cs | 48 +-- src/Infrastructure/Constants.cs | 6 + .../DataFetcher/CurrencyDataFetcher.cs | 92 +++++ src/Infrastructure/DataFetcher/DataFetcher.cs | 350 ------------------ .../DataFetcher/GemDataFetcher.cs | 104 ++++++ .../DataFetcher/IDataFetcher.cs | 5 +- .../DataFetcher/ITempleDataFetcher.cs | 6 + .../DataFetcher/LeagueDataFetcher.cs | 110 ++++++ src/Infrastructure/DataFetcher/PoeToolUrls.cs | 6 +- .../DataFetcher/TempleDataFetcher.cs | 96 +++++ src/Infrastructure/Infrastructure.csproj | 4 +- .../ServiceCollectionExtensions.cs | 6 +- src/Web/Web.csproj | 2 +- test/Application.Test/Application.Test.csproj | 2 +- test/Domain.Test/Domain.Test.csproj | 2 +- .../Infrastructure.Test.csproj | 2 +- test/Web.Test/Web.Test.csproj | 2 +- 27 files changed, 476 insertions(+), 442 deletions(-) create mode 100644 src/Infrastructure/Constants.cs create mode 100644 src/Infrastructure/DataFetcher/CurrencyDataFetcher.cs delete mode 100644 src/Infrastructure/DataFetcher/DataFetcher.cs create mode 100644 src/Infrastructure/DataFetcher/GemDataFetcher.cs create mode 100644 src/Infrastructure/DataFetcher/ITempleDataFetcher.cs create mode 100644 src/Infrastructure/DataFetcher/LeagueDataFetcher.cs create mode 100644 src/Infrastructure/DataFetcher/TempleDataFetcher.cs diff --git a/Directory.Build.props b/Directory.Build.props index bd2f7dc5..e5a1dbf7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,5 +3,6 @@ PoEGamblingHelper.$(MSBuildProjectName) enable enable + true \ No newline at end of file diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index 21fbbf68..8b2e7df3 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -1,9 +1,7 @@ - net7.0 - enable - enable + net8.0 Linux diff --git a/src/Api/Controllers/ApiControllerBase.cs b/src/Api/Controllers/ApiControllerBase.cs index 72856747..25f6795a 100644 --- a/src/Api/Controllers/ApiControllerBase.cs +++ b/src/Api/Controllers/ApiControllerBase.cs @@ -2,8 +2,4 @@ namespace PoEGamblingHelper.Api.Controllers; -[ApiController] -[Route("[controller]")] -public abstract class ApiControllerBase : ControllerBase -{ -} \ No newline at end of file +[ApiController] [Route("[controller]")] public abstract class ApiControllerBase : ControllerBase; \ No newline at end of file diff --git a/src/Api/Controllers/CurrencyController.cs b/src/Api/Controllers/CurrencyController.cs index 4d1c50ad..6b609fc1 100644 --- a/src/Api/Controllers/CurrencyController.cs +++ b/src/Api/Controllers/CurrencyController.cs @@ -2,16 +2,13 @@ using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure; namespace PoEGamblingHelper.Api.Controllers; -public class CurrencyController : ApiControllerBase +public class CurrencyController(ICurrencyRepository currencyRepository) : ApiControllerBase { - private readonly ICurrencyRepository _currencyRepository; - - public CurrencyController(ICurrencyRepository currencyRepository) { _currencyRepository = currencyRepository; } - [HttpGet] - [OutputCache(PolicyName = "FetchData")] - public IAsyncEnumerable GetAll() { return _currencyRepository.GetAll(); } + [OutputCache(PolicyName = Constants.DataFetcherCacheTag)] + public IAsyncEnumerable GetAll() { return currencyRepository.GetAll(); } } \ No newline at end of file diff --git a/src/Api/Controllers/GemController.cs b/src/Api/Controllers/GemController.cs index f0af180d..0afafe8f 100644 --- a/src/Api/Controllers/GemController.cs +++ b/src/Api/Controllers/GemController.cs @@ -3,19 +3,16 @@ using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Infrastructure; namespace PoEGamblingHelper.Api.Controllers; -public class GemController : ApiControllerBase +public class GemController(IGemRepository gemRepository) : ApiControllerBase { - private readonly IGemRepository _gemRepository; - - public GemController(IGemRepository gemRepository) { _gemRepository = gemRepository; } - [HttpGet] - [OutputCache(PolicyName = "FetchData")] + [OutputCache(PolicyName = Constants.DataFetcherCacheTag)] public async Task> GetAll([FromQuery] GemDataQuery? query, [FromQuery] PageRequest page) { - return await _gemRepository.Search(query, page); + return await gemRepository.Search(query, page); } } \ No newline at end of file diff --git a/src/Api/Controllers/LeagueController.cs b/src/Api/Controllers/LeagueController.cs index 7b2f334f..8a6357c6 100644 --- a/src/Api/Controllers/LeagueController.cs +++ b/src/Api/Controllers/LeagueController.cs @@ -2,18 +2,15 @@ using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure; namespace PoEGamblingHelper.Api.Controllers; -public class LeagueController : ApiControllerBase +public class LeagueController(ILeagueRepository leagueRepository) : ApiControllerBase { - private readonly ILeagueRepository _leagueRepository; - - public LeagueController(ILeagueRepository leagueRepository) { _leagueRepository = leagueRepository; } - - [HttpGet] public IAsyncEnumerable GetAll() { return _leagueRepository.GetAllLeagues(); } + [HttpGet] public IAsyncEnumerable GetAll() { return leagueRepository.GetAllLeagues(); } [HttpGet("current")] - [OutputCache(PolicyName = "FetchData")] - public League GetCurrent() { return _leagueRepository.GetCurrent(); } + [OutputCache(PolicyName = Constants.DataFetcherCacheTag)] + public League GetCurrent() { return leagueRepository.GetCurrent(); } } \ No newline at end of file diff --git a/src/Api/Controllers/TempleController.cs b/src/Api/Controllers/TempleController.cs index 34927afd..03a4feb3 100644 --- a/src/Api/Controllers/TempleController.cs +++ b/src/Api/Controllers/TempleController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.OutputCaching; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure; namespace PoEGamblingHelper.Api.Controllers; @@ -11,5 +12,7 @@ public class TempleController : ApiControllerBase public TempleController(ITempleRepository templeRepository) { _templeRepository = templeRepository; } - [HttpGet] [OutputCache(PolicyName = "FetchData")] public TempleCost Get() { return _templeRepository.GetCurrent(); } + [HttpGet] + [OutputCache(PolicyName = Constants.DataFetcherCacheTag)] + public TempleCost Get() { return _templeRepository.GetCurrent(); } } \ No newline at end of file diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index de3fcba9..9a3411a8 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index 62f43190..61798179 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 disable diff --git a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs index b82ed108..10e39df0 100644 --- a/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchLeagueJob.cs @@ -5,28 +5,20 @@ namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; -public class FetchLeagueJob : BackgroundJob +public class FetchLeagueJob(ILogger logger, + ILeagueDataFetcher leagueDataFetcher, + IConfiguration configuration) + : BackgroundJob(configuration) { - private readonly IDataFetcher _dataFetcher; - private readonly ILogger _logger; - - public FetchLeagueJob(ILogger logger, - IDataFetcher dataFetcher, - IConfiguration configuration) : base(configuration) - { - _logger = logger; - _dataFetcher = dataFetcher; - } - protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { try { - await _dataFetcher.FetchCurrentLeague(); + await leagueDataFetcher.Fetch(); } catch (PoeGamblingHelperException e) { - _logger.LogError("{Exception}", e); + logger.LogError("{Exception}", e); } } } \ No newline at end of file diff --git a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs index c63dcd35..c6f2bafb 100644 --- a/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs +++ b/src/Infrastructure/BackgroundJobs/FetchPriceDataJob.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.OutputCaching; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PoEGamblingHelper.Application.Exception; using PoEGamblingHelper.Application.Exception.Abstract; @@ -9,36 +10,25 @@ namespace PoEGamblingHelper.Infrastructure.BackgroundJobs; -public class FetchPriceDataJob : BackgroundJob +public class FetchPriceDataJob(ILogger logger, + IOutputCacheStore cache, + ILeagueRepository leagueRepository, + IConfiguration configuration, + [FromKeyedServices("currency")] IDataFetcher currencyDataFetcher, + [FromKeyedServices("temple")] IDataFetcher templeDataFetcher, + [FromKeyedServices("gem")] IDataFetcher gemDataFetcher) + : BackgroundJob(configuration) { - private readonly IOutputCacheStore _cache; - private readonly string _cacheTag = ""; //TODO - private readonly IDataFetcher _dataFetcher; - private readonly ILeagueRepository _leagueRepository; - private readonly ILogger _logger; - - public FetchPriceDataJob(ILogger logger, - IDataFetcher dataFetcher, - IOutputCacheStore cache, - ILeagueRepository leagueRepository, - IConfiguration configuration) : base(configuration) - { - _logger = logger; - _dataFetcher = dataFetcher; - _cache = cache; - _leagueRepository = leagueRepository; - } - protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) { League league; try { - league = _leagueRepository.GetCurrent(); + league = leagueRepository.GetCurrent(); } catch (NoLeagueDataException e) { - _logger.LogError("Could not Fetch Price Data, because League could not be get: {Exception}", e); + logger.LogError("Could not Fetch Price Data, because League could not be get: {Exception}", e); return; } @@ -46,34 +36,34 @@ protected override async Task ExecuteJobAsync(CancellationToken stoppingToken) try { - await _dataFetcher.FetchCurrencyData(league); + await currencyDataFetcher.Fetch(league); } catch (PoeGamblingHelperException e) { - _logger.LogError("Could not Fetch CurrencyData: {Exception}", e); + logger.LogError("Could not Fetch CurrencyData: {Exception}", e); } try { - await _dataFetcher.FetchTemplePriceData(league); + await templeDataFetcher.Fetch(league); } catch (PoeGamblingHelperException e) { - _logger.LogError("Could not Fetch TemplePriceData: {Exception}", e); + logger.LogError("Could not Fetch TemplePriceData: {Exception}", e); } try { - await _dataFetcher.FetchGemPriceData(league); + await gemDataFetcher.Fetch(league); } catch (PoeGamblingHelperException e) { - _logger.LogError("Could not Fetch GemPriceData: {Exception}", e); + logger.LogError("Could not Fetch GemPriceData: {Exception}", e); } #endregion - await _cache.EvictByTagAsync(_cacheTag, stoppingToken); - _logger.LogDebug("Cache cleared"); + await cache.EvictByTagAsync(Constants.DataFetcherCacheTag, stoppingToken); + logger.LogDebug("Cache cleared"); } } \ No newline at end of file diff --git a/src/Infrastructure/Constants.cs b/src/Infrastructure/Constants.cs new file mode 100644 index 00000000..179bdb31 --- /dev/null +++ b/src/Infrastructure/Constants.cs @@ -0,0 +1,6 @@ +namespace PoEGamblingHelper.Infrastructure; + +public static class Constants +{ + public const string DataFetcherCacheTag = "DataFetcher"; +} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/CurrencyDataFetcher.cs b/src/Infrastructure/DataFetcher/CurrencyDataFetcher.cs new file mode 100644 index 00000000..407373a5 --- /dev/null +++ b/src/Infrastructure/DataFetcher/CurrencyDataFetcher.cs @@ -0,0 +1,92 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Extensions; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.DataFetcher; + +public class CurrencyDataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory, + IHttpClientFactory httpClientFactory) : IDataFetcher +{ + public async Task Fetch(League league) + { + var response = await GetAsync($"{PoeToolUrls.PoeNinjaCurrencyUrl}&league={league.Name}"); + if (!response.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeNinjaCurrencyUrl); + var currencyPriceData = await response.Content.ReadFromJsonAsync(); + if (currencyPriceData is null) throw new ApiDownException(PoeToolUrls.PoeNinjaCurrencyUrl); + logger.LogInformation("Got data from {Result} currency items", currencyPriceData.Lines.Length); + + foreach (var currencyData in currencyPriceData.Lines) // set icons + { + var currencyDetails = currencyPriceData.CurrencyDetails + .FirstOrDefault(currencyDetails => + currencyData.CurrencyTypeName.EqualsIgnoreCase( + currencyDetails.Name)); + if (currencyDetails is not null) currencyData.Icon = currencyDetails.Icon; + } + + await using var applicationDbContext = await applicationDbContextFactory.CreateDbContextAsync(); + + var existingCurrency = applicationDbContext.Currency + .AsNoTracking() + .Select(currency => currency.Id) + .ToArray(); + + var newPoeNinjaCurrencyData = currencyPriceData.Lines + .Where(currencyData => + !existingCurrency.Contains(currencyData.DetailsId)) + .ToArray(); + await applicationDbContext.Currency.AddRangeAsync( + newPoeNinjaCurrencyData.Select(poeNinjaData => poeNinjaData.ToCurrencyData())); + logger.LogInformation("Added {Result} new Currency", newPoeNinjaCurrencyData.Length); + + var updatedPoeNinjaCurrencyData = currencyPriceData.Lines + .Where(gem => existingCurrency.Contains(gem.DetailsId)) + .ToArray(); + applicationDbContext.Currency.UpdateRange( + updatedPoeNinjaCurrencyData.Select(poeNinjaData => poeNinjaData.ToCurrencyData())); + logger.LogInformation("Updated {Result} Currency", updatedPoeNinjaCurrencyData.Length); + + await applicationDbContext.SaveChangesAsync(); + + #region Hardcoded Chaos Orb + + // not EqualsIgnoreCase because of EntityFramework + var chaos = applicationDbContext.Currency + .FirstOrDefault(currency => currency.Name.ToLower().Equals("chaos orb")); + if (chaos is not null) return; + await applicationDbContext.Currency.AddAsync(new Currency + { + Name = "Chaos Orb", + ChaosEquivalent = 1, + Icon = + "https://web.poecdn.com/image/Art/2DItems/Currency/CurrencyRerollRare.png", + Id = "chaos-orb" + }); + logger.LogInformation("Saved Chaos Orb"); + + #endregion + + await applicationDbContext.SaveChangesAsync(); + } + + private async Task GetAsync(string url) + { + try + { + var httpClient = httpClientFactory.CreateClient(); + httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); + httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + return await httpClient.GetAsync(url); + } + catch (HttpRequestException e) + { + throw new ApiDownException(url, e.Message); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/DataFetcher.cs b/src/Infrastructure/DataFetcher/DataFetcher.cs deleted file mode 100644 index 2125423f..00000000 --- a/src/Infrastructure/DataFetcher/DataFetcher.cs +++ /dev/null @@ -1,350 +0,0 @@ -using System.Net; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Text.RegularExpressions; -using HtmlAgilityPack; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using PoEGamblingHelper.Application.Exception; -using PoEGamblingHelper.Application.Extensions; -using PoEGamblingHelper.Domain.Entity; -using PoEGamblingHelper.Infrastructure.Database; -using PoEGamblingHelper.Infrastructure.Extensions; - -namespace PoEGamblingHelper.Infrastructure.DataFetcher; - -public partial class DataFetcher : IDataFetcher -{ - private readonly IDbContextFactory _applicationDbContextFactory; - private readonly HtmlWeb _htmlLoader = new(); - private readonly IHttpClientFactory _httpClientFactory; - private readonly MediaTypeHeaderValue _jsonMediaTypeHeader = MediaTypeHeaderValue.Parse("application/json"); - private readonly ILogger _logger; - private readonly string _templeQuery; - - public DataFetcher(ILogger logger, - IDbContextFactory applicationDbContextFactory, - IHttpClientFactory httpClientFactory) - { - _logger = logger; - _applicationDbContextFactory = applicationDbContextFactory; - _httpClientFactory = httpClientFactory; - _templeQuery = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/TempleQuery.json"); - } - - public async Task FetchCurrentLeague() - { - HtmlDocument doc; - try - { - doc = _htmlLoader.Load(PoeToolUrls.PoeDbLeagueUrl); - } - catch (WebException e) - { - throw new ApiDownException(PoeToolUrls.PoeDbLeagueUrl, e.Message); - } - - if (doc is null) throw new ApiDownException(PoeToolUrls.PoeDbLeagueUrl); - var tables = doc.DocumentNode.SelectNodes("//table"); - if (tables is null) throw new PoeDbCannotParseException("No tables found"); - var leaguesTable = tables.FirstOrDefault(n => n.HasChildNodes && n.InnerHtml.Contains("Weeks")); - if (leaguesTable is null) throw new PoeDbCannotParseException("No league table found"); - - var leagueRows = leaguesTable.SelectNodes(".//tr").Where(n => n.HasChildNodes).ToArray(); - if (leagueRows.Length == 0) throw new PoeDbCannotParseException("No league rows found"); - - var titleRow = leagueRows[0]; - var (versionColumn, nameColumn, releaseColumn) = GetIndexesFromTitleRow(titleRow); - - await ParseLeagueRows( - leagueRows.Skip(1).Where(row => row.HasChildNodes), - releaseColumn, - nameColumn, - versionColumn - ); - } - - public async Task FetchCurrencyData(League league) - { - var response = await GetAsync($"{PoeToolUrls.PoeNinjaCurrencyUrl}&league={league.Name}"); - if (!response.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeNinjaCurrencyUrl); - var currencyPriceData = await response.Content.ReadFromJsonAsync(); - if (currencyPriceData is null) throw new ApiDownException(PoeToolUrls.PoeNinjaCurrencyUrl); - _logger.LogInformation("Got data from {Result} currency items", currencyPriceData.Lines.Length); - - // set icons - foreach (var currencyData in currencyPriceData.Lines) - { - var currencyDetails = - currencyPriceData.CurrencyDetails - .FirstOrDefault(currencyDetails => - currencyData.CurrencyTypeName.EqualsIgnoreCase( - currencyDetails.Name)); - if (currencyDetails is not null) currencyData.Icon = currencyDetails.Icon; - } - - await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); - var existingCurrency = applicationDbContext.Currency.Select(currency => currency.Id).ToArray(); - applicationDbContext.ClearTrackedEntities(); - - var newPoeNinjaCurrencyData = currencyPriceData.Lines - .Where(currencyData => - !existingCurrency.Contains(currencyData.DetailsId)) - .ToArray(); - await applicationDbContext.Currency.AddRangeAsync( - newPoeNinjaCurrencyData.Select(poeNinjaData => poeNinjaData.ToCurrencyData())); - _logger.LogInformation("Added {Result} new Currency", newPoeNinjaCurrencyData.Length); - - var updatedPoeNinjaCurrencyData = - currencyPriceData.Lines.Where(gem => existingCurrency.Contains(gem.DetailsId)).ToArray(); - applicationDbContext.Currency.UpdateRange( - updatedPoeNinjaCurrencyData.Select(poeNinjaData => poeNinjaData.ToCurrencyData())); - _logger.LogInformation("Updated {Result} Currency", updatedPoeNinjaCurrencyData.Length); - - await applicationDbContext.SaveChangesAsync(); - - #region Hardcoded Chaos Orb - - // not EqualsIgnoreCase because of EntityFramework - var chaos = applicationDbContext.Currency - .FirstOrDefault(currency => currency.Name.ToLower().Equals("chaos orb")); - if (chaos is not null) return; - await applicationDbContext.Currency.AddAsync(new Currency - { - Name = "Chaos Orb", - ChaosEquivalent = 1, - Icon = - "https://web.poecdn.com/image/Art/2DItems/Currency/CurrencyRerollRare.png", - Id = "chaos-orb" - }); - _logger.LogInformation("Saved Chaos Orb"); - - #endregion - - await applicationDbContext.SaveChangesAsync(); - } - - public async Task FetchTemplePriceData(League league) - { - #region Fetch Temple Fetch - - _logger.LogDebug("Fetching Temple IDs..."); - - TradeResults temples; - var requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/search/{league.Name}"; - using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri)) - { - request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); - var result = await SendAsync(request); - if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); - - temples = await result.Content.ReadFromJsonAsync() ?? - throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); - } - - _logger.LogDebug("Found {ResultLength} Temples IDs", temples.Result.Length); - - #endregion - - #region Fetch Temples - - var takeAmount = Math.Min(10, temples.Result.Length); - var skipAmount = takeAmount == temples.Result.Length ? 0 : 2; - var itemQuery = string.Join(",", temples.Result.Skip(skipAmount).Take(takeAmount)); - - _logger.LogDebug("Skipping {SkipAmount} and fetching {TakeAmount} Temples...", skipAmount, takeAmount); - - TradeEntryResult priceResults; - requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/fetch/{itemQuery}?query={temples.Id}"; - using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) - { - request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); - var result = await SendAsync(request); - if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); - - priceResults = await result.Content.ReadFromJsonAsync() - ?? throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); - } - - _logger.LogDebug("Found {ResultLength} TemplePrices", priceResults.Result.Length); - - #endregion - - await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); - var chaosValues = priceResults.Result - .Select(priceResult => - priceResult.Listing - .Price - .ChaosAmount(applicationDbContext.Currency) - ) - .ToArray(); - - await applicationDbContext.TempleCost.ExecuteDeleteAsync(); // Delete every Temple Entry - await applicationDbContext.TempleCost.AddAsync(new TempleCost { ChaosValue = chaosValues }); - await applicationDbContext.SaveChangesAsync(); - - _logger.LogInformation("Saved {PriceLength} TemplePrices", chaosValues.Length); - } - - public async Task FetchGemPriceData(League league) - { - var response = await GetAsync(PoeToolUrls.PoeNinjaGemUrl + $"&league={league.Name}"); - if (!response.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeNinjaGemUrl); - var gemPriceData = await response.Content.ReadFromJsonAsync(); - if (gemPriceData is null) throw new ApiDownException(PoeToolUrls.PoeNinjaGemUrl); - _logger.LogInformation("Got data from {Result} gems", gemPriceData.Lines.Length); - - // GemTradeData - await using (var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync()) - { - var existingGemTradeData = - applicationDbContext.GemTradeData.Select(gemTradeData => gemTradeData.Id).ToArray(); - applicationDbContext.ClearTrackedEntities(); - - var newGemTradeData = gemPriceData.Lines.Where(gem => !existingGemTradeData.Contains(gem.Id)).ToArray(); - await applicationDbContext.GemTradeData.AddRangeAsync( - newGemTradeData.Select(gemTradeData => gemTradeData.ToGemTradeData())); - _logger.LogInformation("Added {Result} new GemTradeData", newGemTradeData.Length); - - var updatedGemTradeData = gemPriceData.Lines.Where(gem => existingGemTradeData.Contains(gem.Id)).ToArray(); - applicationDbContext.GemTradeData.UpdateRange( - updatedGemTradeData.Select(gemTradeData => gemTradeData.ToGemTradeData())); - _logger.LogInformation("Updated {Result} GemTradeData", updatedGemTradeData.Length); - await applicationDbContext.SaveChangesAsync(); - } - - // GemData - await using (var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync()) - { - var existingGemData = applicationDbContext.GemData.ToArray(); - var existingGemDataNames = existingGemData.Select(gem => gem.Name.ToLowerInvariant().Trim()).ToArray(); - applicationDbContext.ClearTrackedEntities(); - - var allGemTradeData = applicationDbContext.GemTradeData.ToArray(); - var groupedPoeNinjaData = gemPriceData.Lines.GroupBy(priceData => priceData.Name).ToArray(); - - var newGemData = groupedPoeNinjaData - .Where(group => !existingGemDataNames.Contains(group.Key.ToLowerInvariant().Trim())) - .ToArray(); - await applicationDbContext.GemData.AddRangeAsync( - newGemData.Select(group => group.ToGemData(allGemTradeData))); - _logger.LogInformation("Added {Result} GemData", newGemData.Length); - - var updatedGemData = groupedPoeNinjaData - .Where(group => existingGemDataNames.Contains(group.Key.ToLowerInvariant().Trim())) - .ToArray(); - applicationDbContext.GemData.UpdateRange( - updatedGemData.Select(group => group.ToGemData(allGemTradeData, existingGemData)) - ); - _logger.LogInformation("Updated {Result} GemData", updatedGemData.Length); - - await applicationDbContext.SaveChangesAsync(); - } - } - - #region Helper Methods - - private static (int versionColumn, int nameColumn, int releaseColumn) GetIndexesFromTitleRow(HtmlNode titleRow) - { - var versionColumn = titleRow.ChildNodes.First(td => td.InnerText.EqualsIgnoreCase("version")); - if (versionColumn is null) throw new PoeDbCannotParseException("no version column in league table"); - var versionColumnIndex = titleRow.ChildNodes.IndexOf(versionColumn); - - var nameColumn = titleRow.ChildNodes.First(td => td.InnerText.EqualsIgnoreCase("league")); - if (nameColumn is null) throw new PoeDbCannotParseException("no name column in league table"); - var nameColumnIndex = titleRow.ChildNodes.IndexOf(nameColumn); - - var releaseColumn = titleRow.ChildNodes.First(td => td.InnerText.EqualsIgnoreCase("international")); - if (releaseColumn is null) throw new PoeDbCannotParseException("no release column in league table"); - var releaseColumnIndex = titleRow.ChildNodes.IndexOf(releaseColumn); - - return (versionColumnIndex, nameColumnIndex, releaseColumnIndex); - } - - private async Task ParseLeagueRows(IEnumerable leagueRows, - int releaseColumnIndex, - int nameColumnIndex, - int versionColumnIndex) - { - var yearRegex = YearRegex(); - var fullDateRegex = FullDateRegex(); - var nameExpansionRegex = NameExpansionRegex(); - - await using var applicationDbContext = await _applicationDbContextFactory.CreateDbContextAsync(); - foreach (var row in leagueRows) - { - var releaseDateText = row.ChildNodes[releaseColumnIndex].InnerText; - var date = DateTime.SpecifyKind( - yearRegex.IsMatch(releaseDateText) - ? new DateTime(int.Parse(releaseDateText), 12, 31) - : fullDateRegex.IsMatch(releaseDateText) - ? DateTime.Parse(releaseDateText) - : DateTime.MaxValue, - DateTimeKind.Utc - ); - - var name = nameExpansionRegex.Replace(row.ChildNodes[nameColumnIndex].InnerText, "").Trim(); - var version = row.ChildNodes[versionColumnIndex].InnerText; - - // not EqualsIgnoreCase because of EntityFramework - var dbLeague = applicationDbContext.League - .FirstOrDefault( - dbLeague => dbLeague.Version.ToLower().Equals(version.ToLower()) - ); - - if (dbLeague is null) - { - var league = new League { Name = name, StartDate = date, Version = version }; - await applicationDbContext.League.AddAsync(league); - _logger.LogInformation("Saved League: {League}", league); - continue; - } - - dbLeague.Name = name; - dbLeague.StartDate = date; - dbLeague.Version = version; - applicationDbContext.League.Update(dbLeague); - _logger.LogInformation("Updated League: {League}", dbLeague); - } - - await applicationDbContext.SaveChangesAsync(); - } - - private async Task GetAsync(string url) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); - httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - return await httpClient.GetAsync(url); - } - catch (HttpRequestException e) - { - throw new ApiDownException(url, e.Message); - } - } - - private async Task SendAsync(HttpRequestMessage request) - { - try - { - var httpClient = _httpClientFactory.CreateClient(); - httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); - httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); - return await httpClient.SendAsync(request); - } - catch (HttpRequestException e) - { - throw new ApiDownException(request.RequestUri!.Host, e.Message); - } - } - - [GeneratedRegex("^\\d\\d\\d\\d$")] private static partial Regex YearRegex(); - - [GeneratedRegex("^\\d\\d\\d\\d-\\d\\d-\\d\\d$")] private static partial Regex FullDateRegex(); - - [GeneratedRegex("<.+>")] private static partial Regex NameExpansionRegex(); - - #endregion -} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/GemDataFetcher.cs b/src/Infrastructure/DataFetcher/GemDataFetcher.cs new file mode 100644 index 00000000..93fd22b1 --- /dev/null +++ b/src/Infrastructure/DataFetcher/GemDataFetcher.cs @@ -0,0 +1,104 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; +using PoEGamblingHelper.Infrastructure.Extensions; + +namespace PoEGamblingHelper.Infrastructure.DataFetcher; + +public class GemDataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory, + IHttpClientFactory httpClientFactory) : IDataFetcher +{ + public async Task Fetch(League league) + { + var response = await GetAsync(PoeToolUrls.PoeNinjaGemUrl + $"&league={league.Name}"); + if (!response.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeNinjaGemUrl); + var gemPriceData = await response.Content.ReadFromJsonAsync(); + if (gemPriceData is null) throw new ApiDownException(PoeToolUrls.PoeNinjaGemUrl); + logger.LogInformation("Got data from {Result} gems", gemPriceData.Lines.Length); + + await FetchGemTradeData(gemPriceData); + await FetchGemData(gemPriceData); + } + + private async Task FetchGemData(GemPriceData gemPriceData) + { + await using var applicationDbContext = await applicationDbContextFactory.CreateDbContextAsync(); + + var existingGemData = applicationDbContext.GemData + .AsNoTracking() + .ToArray(); + var existingGemDataNames = existingGemData.Select(gem => gem.Name.ToLowerInvariant().Trim()) + .ToArray(); + + var allGemTradeData = applicationDbContext.GemTradeData.ToArray(); + var groupedPoeNinjaData = gemPriceData.Lines.GroupBy(priceData => priceData.Name).ToArray(); + + // Add + var newGemData = groupedPoeNinjaData + .Where(group => !existingGemDataNames.Contains(group.Key.ToLowerInvariant().Trim())) + .ToArray(); + await applicationDbContext.GemData.AddRangeAsync( + newGemData.Select(group => group.ToGemData(allGemTradeData))); + logger.LogInformation("Added {Result} GemData", newGemData.Length); + + // Update + var updatedGemData = groupedPoeNinjaData + .Where(group => existingGemDataNames.Contains(group.Key.ToLowerInvariant().Trim())) + .ToArray(); + applicationDbContext.GemData.UpdateRange( + updatedGemData.Select(group => group.ToGemData(allGemTradeData, existingGemData)) + ); + logger.LogInformation("Updated {Result} GemData", updatedGemData.Length); + + await applicationDbContext.SaveChangesAsync(); + } + + private async Task FetchGemTradeData(GemPriceData gemPriceData) + { + await using var applicationDbContext = await applicationDbContextFactory.CreateDbContextAsync(); + + var existingGemTradeData = applicationDbContext.GemTradeData + .AsNoTracking() + .Select(gemTradeData => gemTradeData.Id) + .ToArray(); + + var newGemTradeData = gemPriceData.Lines + .Where(gem => !existingGemTradeData.Contains(gem.Id)) + .ToArray(); + + // Add + await applicationDbContext.GemTradeData.AddRangeAsync( + newGemTradeData.Select(gemTradeData => gemTradeData.ToGemTradeData())); + logger.LogInformation("Added {Result} new GemTradeData", newGemTradeData.Length); + + // Update + var updatedGemTradeData = gemPriceData.Lines + .Where(gem => existingGemTradeData.Contains(gem.Id)) + .ToArray(); + applicationDbContext.GemTradeData.UpdateRange( + updatedGemTradeData.Select(gemTradeData => gemTradeData.ToGemTradeData())); + logger.LogInformation("Updated {Result} GemTradeData", updatedGemTradeData.Length); + + await applicationDbContext.SaveChangesAsync(); + } + + private async Task GetAsync(string url) + { + try + { + var httpClient = httpClientFactory.CreateClient(); + httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); + httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + return await httpClient.GetAsync(url); + } + catch (HttpRequestException e) + { + throw new ApiDownException(url, e.Message); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/IDataFetcher.cs b/src/Infrastructure/DataFetcher/IDataFetcher.cs index 08a8112f..950d7c73 100644 --- a/src/Infrastructure/DataFetcher/IDataFetcher.cs +++ b/src/Infrastructure/DataFetcher/IDataFetcher.cs @@ -4,8 +4,5 @@ namespace PoEGamblingHelper.Infrastructure.DataFetcher; public interface IDataFetcher { - Task FetchCurrentLeague(); - Task FetchCurrencyData(League league); - Task FetchTemplePriceData(League league); - Task FetchGemPriceData(League league); + Task Fetch(League league); } \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/ITempleDataFetcher.cs b/src/Infrastructure/DataFetcher/ITempleDataFetcher.cs new file mode 100644 index 00000000..a42740a1 --- /dev/null +++ b/src/Infrastructure/DataFetcher/ITempleDataFetcher.cs @@ -0,0 +1,6 @@ +namespace PoEGamblingHelper.Infrastructure.DataFetcher; + +public interface ILeagueDataFetcher +{ + Task Fetch(); +} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs new file mode 100644 index 00000000..33375935 --- /dev/null +++ b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs @@ -0,0 +1,110 @@ +using System.Net; +using System.Text.RegularExpressions; +using HtmlAgilityPack; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Application.Extensions; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.DataFetcher; + +public partial class LeagueDataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory) + : ILeagueDataFetcher +{ + private static readonly Regex Regex1 = new Regex("^\\d\\d\\d\\d$"); + private readonly HtmlWeb _htmlLoader = new(); + + public async Task Fetch() + { + HtmlDocument doc; + try + { + doc = _htmlLoader.Load(PoeToolUrls.PoeDbLeagueUrl); + } + catch (WebException e) + { + throw new ApiDownException(PoeToolUrls.PoeDbLeagueUrl, e.Message); + } + + if (doc is null) throw new ApiDownException(PoeToolUrls.PoeDbLeagueUrl); + var tables = doc.DocumentNode.SelectNodes("//table"); + if (tables is null) throw new PoeDbCannotParseException("No tables found"); + var leaguesTable = tables.FirstOrDefault(n => n.HasChildNodes && n.InnerHtml.Contains("Weeks")); + if (leaguesTable is null) throw new PoeDbCannotParseException("No league table found"); + + var leagueRows = leaguesTable.SelectNodes(".//tr").Where(n => n.HasChildNodes).ToArray(); + if (leagueRows.Length == 0) throw new PoeDbCannotParseException("No league rows found"); + + var titleRow = leagueRows[0]; + + await ParseLeagueRows( + leagueRows.Skip(1).Where(row => row.HasChildNodes), + GetColumnIndexFromTitleRow(titleRow, "international"), + GetColumnIndexFromTitleRow(titleRow, "league"), + GetColumnIndexFromTitleRow(titleRow, "version") + ); + } + + #region Helper Methods + + private static int GetColumnIndexFromTitleRow(HtmlNode titleRow, string column) + { + var result = titleRow.ChildNodes.First(td => td.InnerText.EqualsIgnoreCase(column)); + if (result is null) throw new PoeDbCannotParseException($"no {column} column in league table"); + return titleRow.ChildNodes.IndexOf(result); + } + + private async Task ParseLeagueRows(IEnumerable leagueRows, + int releaseColumnIndex, + int nameColumnIndex, + int versionColumnIndex) + { + await using var applicationDbContext = await applicationDbContextFactory.CreateDbContextAsync(); + + foreach (var row in leagueRows) + { + var releaseDateText = row.ChildNodes[releaseColumnIndex].InnerText; + var date = DateTime.SpecifyKind( + YearRegex.IsMatch(releaseDateText) + ? new DateTime(int.Parse(releaseDateText), 12, 31) + : FullDateRegex.IsMatch(releaseDateText) + ? DateTime.Parse(releaseDateText) + : DateTime.MaxValue, + DateTimeKind.Utc + ); + + var name = NameExpansionRegex.Replace(row.ChildNodes[nameColumnIndex].InnerText, "").Trim(); + var version = row.ChildNodes[versionColumnIndex].InnerText; + + // not EqualsIgnoreCase because of EntityFramework + var dbLeague = applicationDbContext.League + .FirstOrDefault( + dbLeague => dbLeague.Version.ToLower().Equals(version.ToLower())); + + if (dbLeague is null) + { + var league = new League { Name = name, StartDate = date, Version = version }; + await applicationDbContext.League.AddAsync(league); + logger.LogInformation("Saved League: {League}", league); + continue; + } + + dbLeague.Name = name; + dbLeague.StartDate = date; + dbLeague.Version = version; + applicationDbContext.League.Update(dbLeague); + logger.LogInformation("Updated League: {League}", dbLeague); + } + + await applicationDbContext.SaveChangesAsync(); + } + + private static readonly Regex YearRegex = new(@"^\d\d\d\d$"); + private static readonly Regex FullDateRegex = new(@"^\d\d\d\d-\d\d-\d\d$"); + private static readonly Regex NameExpansionRegex = new("<.+>"); + + #endregion +} \ No newline at end of file diff --git a/src/Infrastructure/DataFetcher/PoeToolUrls.cs b/src/Infrastructure/DataFetcher/PoeToolUrls.cs index d2928ec0..f4d2914f 100644 --- a/src/Infrastructure/DataFetcher/PoeToolUrls.cs +++ b/src/Infrastructure/DataFetcher/PoeToolUrls.cs @@ -2,9 +2,9 @@ public static class PoeToolUrls { - public const string PoeApiUrl = "https://www.pathofexile.com/api"; - public const string PoeDbUrl = "https://poedb.tw/us"; - public const string PoeNinjaUrl = "https://poe.ninja/api/data"; + private const string PoeApiUrl = "https://www.pathofexile.com/api"; + private const string PoeDbUrl = "https://poedb.tw/us"; + private const string PoeNinjaUrl = "https://poe.ninja/api/data"; public static string PoeDbLeagueUrl => $"{PoeDbUrl}/League#LeaguesList"; public static string PoeNinjaCurrencyUrl => $"{PoeNinjaUrl}/currencyoverview?type=Currency"; public static string PoeNinjaGemUrl => $"{PoeNinjaUrl}/itemoverview?type=SkillGem"; diff --git a/src/Infrastructure/DataFetcher/TempleDataFetcher.cs b/src/Infrastructure/DataFetcher/TempleDataFetcher.cs new file mode 100644 index 00000000..98ab626b --- /dev/null +++ b/src/Infrastructure/DataFetcher/TempleDataFetcher.cs @@ -0,0 +1,96 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PoEGamblingHelper.Application.Exception; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Infrastructure.Database; + +namespace PoEGamblingHelper.Infrastructure.DataFetcher; + +public class TempleDataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory, + IHttpClientFactory httpClientFactory) : IDataFetcher +{ + private readonly MediaTypeHeaderValue _jsonMediaTypeHeader = MediaTypeHeaderValue.Parse("application/json"); + + private readonly string _templeQuery = + File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/TempleQuery.json"); + + public async Task Fetch(League league) + { + #region Fetch Temple IDs + + logger.LogDebug("Fetching Temple IDs..."); + + TradeResults temples; + var requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/search/{league.Name}"; + using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri)) + { + request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); + var result = await SendAsync(request); + if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); + + temples = await result.Content.ReadFromJsonAsync() ?? + throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); + } + + logger.LogDebug("Found {ResultLength} Temples IDs", temples.Result.Length); + + #endregion + + #region Fetch Temples + + var takeAmount = Math.Min(10, temples.Result.Length); + var skipAmount = takeAmount == temples.Result.Length ? 0 : 2; + var itemQuery = string.Join(",", temples.Result.Skip(skipAmount).Take(takeAmount)); + + logger.LogDebug("Skipping {SkipAmount} and fetching {TakeAmount} Temples...", skipAmount, takeAmount); + + TradeEntryResult priceResults; + requestUri = $"{PoeToolUrls.PoeApiTradeUrl}/fetch/{itemQuery}?query={temples.Id}"; + using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) + { + request.Content = new StringContent(_templeQuery, _jsonMediaTypeHeader); + var result = await SendAsync(request); + if (!result.IsSuccessStatusCode) throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); + + priceResults = await result.Content.ReadFromJsonAsync() + ?? throw new ApiDownException(PoeToolUrls.PoeApiTradeUrl); + } + + logger.LogDebug("Found {ResultLength} TemplePrices", priceResults.Result.Length); + + #endregion + + await using var applicationDbContext = await applicationDbContextFactory.CreateDbContextAsync(); + var chaosValues = priceResults.Result + .Select(priceResult => + priceResult.Listing + .Price + .ChaosAmount(applicationDbContext.Currency) + ) + .ToArray(); + + await applicationDbContext.TempleCost.ExecuteDeleteAsync(); // Delete every Temple Entry + await applicationDbContext.TempleCost.AddAsync(new TempleCost { ChaosValue = chaosValues }); + await applicationDbContext.SaveChangesAsync(); + + logger.LogInformation("Saved {PriceLength} TemplePrices", chaosValues.Length); + } + + private async Task SendAsync(HttpRequestMessage request) + { + try + { + var httpClient = httpClientFactory.CreateClient(); + httpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("PoEGamblingHelper/1.0.0")); + httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + return await httpClient.SendAsync(request); + } + catch (HttpRequestException e) + { + throw new ApiDownException(request.RequestUri!.Host, e.Message); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 13d4ca68..fd10380b 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -1,9 +1,7 @@ - net7.0 - enable - enable + net8.0 diff --git a/src/Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/ServiceCollectionExtensions.cs index 694d3c82..44267a21 100644 --- a/src/Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/ServiceCollectionExtensions.cs @@ -26,10 +26,14 @@ public static void AddInfrastructureServices(this IServiceCollection services, I services.AddTransient(); services.AddTransient(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddKeyedTransient("currency"); + services.AddKeyedTransient("gem"); + services.AddKeyedTransient("temple"); + services.AddBackgroundJobs(); } diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 96b0a776..ffb95043 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/test/Application.Test/Application.Test.csproj b/test/Application.Test/Application.Test.csproj index 69beec7a..d7749f35 100644 --- a/test/Application.Test/Application.Test.csproj +++ b/test/Application.Test/Application.Test.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/test/Domain.Test/Domain.Test.csproj b/test/Domain.Test/Domain.Test.csproj index 06026c50..aba5a273 100644 --- a/test/Domain.Test/Domain.Test.csproj +++ b/test/Domain.Test/Domain.Test.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/test/Infrastructure.Test/Infrastructure.Test.csproj b/test/Infrastructure.Test/Infrastructure.Test.csproj index 4cbf30ef..6a432abe 100644 --- a/test/Infrastructure.Test/Infrastructure.Test.csproj +++ b/test/Infrastructure.Test/Infrastructure.Test.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/test/Web.Test/Web.Test.csproj b/test/Web.Test/Web.Test.csproj index 3e30024f..cb8f9876 100644 --- a/test/Web.Test/Web.Test.csproj +++ b/test/Web.Test/Web.Test.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable From 3789c36f152122719ae0b4fdb8882582f900e2fc Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 22:58:34 +0200 Subject: [PATCH 050/129] dotnet 8 autocleanup --- src/Infrastructure/DataFetcher/LeagueDataFetcher.cs | 6 +++--- test/Application.Test/Services/GemServiceTest.cs | 6 +----- test/Application.Test/Services/InitServiceTest.cs | 3 --- test/Application.Test/Services/LeagueServiceTest.cs | 4 +--- test/Application.Test/Util/ExtensionMethodsTest.cs | 6 +----- test/Domain.Test/Entity/TempleCostTest.cs | 1 - test/Web.Test/Util/ExtensionFunctionsTest.cs | 2 -- 7 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs index 33375935..188c2bc7 100644 --- a/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs +++ b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs @@ -10,11 +10,11 @@ namespace PoEGamblingHelper.Infrastructure.DataFetcher; -public partial class LeagueDataFetcher(ILogger logger, - IDbContextFactory applicationDbContextFactory) +public class LeagueDataFetcher(ILogger logger, + IDbContextFactory applicationDbContextFactory) : ILeagueDataFetcher { - private static readonly Regex Regex1 = new Regex("^\\d\\d\\d\\d$"); + private static readonly Regex Regex1 = new("^\\d\\d\\d\\d$"); private readonly HtmlWeb _htmlLoader = new(); public async Task Fetch() diff --git a/test/Application.Test/Services/GemServiceTest.cs b/test/Application.Test/Services/GemServiceTest.cs index 97b0d867..d34dd81b 100644 --- a/test/Application.Test/Services/GemServiceTest.cs +++ b/test/Application.Test/Services/GemServiceTest.cs @@ -1,8 +1,4 @@ -using MockQueryable.Moq; -using Moq; -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Application.Test.Services; +namespace PoEGamblingHelper.Application.Test.Services; public class GemServiceTest { diff --git a/test/Application.Test/Services/InitServiceTest.cs b/test/Application.Test/Services/InitServiceTest.cs index 313ef98d..98457aed 100644 --- a/test/Application.Test/Services/InitServiceTest.cs +++ b/test/Application.Test/Services/InitServiceTest.cs @@ -1,8 +1,5 @@ using System.Diagnostics; -using Microsoft.Extensions.Logging; -using Moq; using PoEGamblingHelper.Application.Exception; -using PoEGamblingHelper.Domain.Entity; #pragma warning disable CS8625 diff --git a/test/Application.Test/Services/LeagueServiceTest.cs b/test/Application.Test/Services/LeagueServiceTest.cs index 6803b5e1..1738d80f 100644 --- a/test/Application.Test/Services/LeagueServiceTest.cs +++ b/test/Application.Test/Services/LeagueServiceTest.cs @@ -1,6 +1,4 @@ -using MockQueryable.Moq; -using PoEGamblingHelper.Application.Exception; -using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Application.Exception; namespace PoEGamblingHelper.Application.Test.Services; diff --git a/test/Application.Test/Util/ExtensionMethodsTest.cs b/test/Application.Test/Util/ExtensionMethodsTest.cs index 73240b9c..90c043ce 100644 --- a/test/Application.Test/Util/ExtensionMethodsTest.cs +++ b/test/Application.Test/Util/ExtensionMethodsTest.cs @@ -1,8 +1,4 @@ -using FluentAssertions; -using PoEGamblingHelper.Application.Extensions; -using PoEGamblingHelper.Domain.Entity.Gem; - -namespace PoEGamblingHelper.Application.Test.Util; +namespace PoEGamblingHelper.Application.Test.Util; public class ExtensionMethodsTest { diff --git a/test/Domain.Test/Entity/TempleCostTest.cs b/test/Domain.Test/Entity/TempleCostTest.cs index 2a00dcd7..c36ae013 100644 --- a/test/Domain.Test/Entity/TempleCostTest.cs +++ b/test/Domain.Test/Entity/TempleCostTest.cs @@ -1,4 +1,3 @@ -using FluentAssertions; using PoEGamblingHelper.Domain.Entity; namespace Domain.Test.Entity; diff --git a/test/Web.Test/Util/ExtensionFunctionsTest.cs b/test/Web.Test/Util/ExtensionFunctionsTest.cs index 7dd0caa5..63fc44f2 100644 --- a/test/Web.Test/Util/ExtensionFunctionsTest.cs +++ b/test/Web.Test/Util/ExtensionFunctionsTest.cs @@ -1,7 +1,5 @@ using System.Globalization; -using FluentAssertions; using PoEGamblingHelper.Web.Util; -using Xunit.Abstractions; namespace Web.Test.Util; From c45895ad433781c5073a6a58d3e4c666544ef673 Mon Sep 17 00:00:00 2001 From: Swerik Date: Mon, 18 Sep 2023 22:59:08 +0200 Subject: [PATCH 051/129] cleanup --- src/Infrastructure/DataFetcher/LeagueDataFetcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs index 188c2bc7..157ed98e 100644 --- a/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs +++ b/src/Infrastructure/DataFetcher/LeagueDataFetcher.cs @@ -14,7 +14,6 @@ public class LeagueDataFetcher(ILogger logger, IDbContextFactory applicationDbContextFactory) : ILeagueDataFetcher { - private static readonly Regex Regex1 = new("^\\d\\d\\d\\d$"); private readonly HtmlWeb _htmlLoader = new(); public async Task Fetch() From 9b20f03f8766a90931dfb54c0f68727eef76cb70 Mon Sep 17 00:00:00 2001 From: Swerik Date: Tue, 19 Sep 2023 23:12:51 +0200 Subject: [PATCH 052/129] Extract Extensions --- .../Extensions/GemDataExtensions.cs | 12 +- .../Extensions/StringExtensions.cs | 6 +- src/Web/Extensions/DecimalExtensions.cs | 16 ++ src/Web/Extensions/HttpContentExtensions.cs | 13 ++ src/Web/Extensions/QueryExtensions.cs | 100 ++++++++++++ src/Web/Extensions/StringifyExtensions.cs | 36 +++++ src/Web/Pages/GamblingHelper.razor.cs | 8 +- src/Web/Properties/launchSettings.json | 2 +- .../Implementations/CurrencyService.cs | 4 +- .../Services/Implementations/GemService.cs | 7 +- .../Services/Implementations/LeagueService.cs | 4 +- .../Implementations/TempleCostService.cs | 4 +- src/Web/Services/Interfaces/HttpService.cs | 14 +- .../Shared/Components/CurrencyDropDown.razor | 27 ---- src/Web/Shared/Components/Filter.razor.cs | 47 +++--- src/Web/Shared/Components/GemStats.razor.cs | 2 +- src/Web/Shared/Components/Navbar.razor | 2 +- src/Web/Util/ExtensionMethods.cs | 148 ------------------ src/Web/Web.csproj | 1 + src/Web/_Imports.razor | 2 +- .../Services/InitServiceTest.cs | 3 + .../Util/ExtensionMethodsTest.cs | 4 +- 22 files changed, 226 insertions(+), 236 deletions(-) create mode 100644 src/Web/Extensions/DecimalExtensions.cs create mode 100644 src/Web/Extensions/HttpContentExtensions.cs create mode 100644 src/Web/Extensions/QueryExtensions.cs create mode 100644 src/Web/Extensions/StringifyExtensions.cs delete mode 100644 src/Web/Shared/Components/CurrencyDropDown.razor delete mode 100644 src/Web/Util/ExtensionMethods.cs diff --git a/src/Application/Extensions/GemDataExtensions.cs b/src/Application/Extensions/GemDataExtensions.cs index 4161603d..34fe72ad 100644 --- a/src/Application/Extensions/GemDataExtensions.cs +++ b/src/Application/Extensions/GemDataExtensions.cs @@ -28,10 +28,10 @@ public static decimal Profit(this GemData gemData, return gemData.Profit(gemData.Value(resultCase), rawCost, templeCost); } - private static decimal Profit(this GemData gemData, - decimal resultValue, - decimal? rawCost = null, - decimal templeCost = 0) + public static decimal Profit(this GemData gemData, + decimal resultValue, + decimal? rawCost = null, + decimal templeCost = 0) { return resultValue - gemData.CostPerTry(rawCost, templeCost); } @@ -40,7 +40,7 @@ private static decimal Profit(this GemData gemData, /// Finds the ChaosValue of the Gem for the specified resultCase. /// If there are multiple, it just gets the cheapest one. /// - private static decimal Value(this GemData gemData, ResultCase resultCase) + public static decimal Value(this GemData gemData, ResultCase resultCase) { return gemData.ResultValue(gemData.MaxLevel() + resultCase.LevelModifier()); } @@ -70,7 +70,7 @@ public static int MaxLevel(this GemData gemData) 20; } - private static decimal CostPerTry(this GemData gemData, decimal? rawCost = null, decimal templeCost = 0) + public static decimal CostPerTry(this GemData gemData, decimal? rawCost = null, decimal templeCost = 0) { return (rawCost ?? gemData.RawCost()) + templeCost; } diff --git a/src/Application/Extensions/StringExtensions.cs b/src/Application/Extensions/StringExtensions.cs index 521f6a0b..786eee10 100644 --- a/src/Application/Extensions/StringExtensions.cs +++ b/src/Application/Extensions/StringExtensions.cs @@ -2,9 +2,9 @@ public static class StringExtensions { - public static bool EqualsIgnoreCase(this string? a, string? b) + public static bool EqualsIgnoreCase(this string? source, string? value) { - return a?.Equals(b, StringComparison.InvariantCultureIgnoreCase) - ?? b is null; + return source?.Equals(value, StringComparison.InvariantCultureIgnoreCase) + ?? value is null; } } \ No newline at end of file diff --git a/src/Web/Extensions/DecimalExtensions.cs b/src/Web/Extensions/DecimalExtensions.cs new file mode 100644 index 00000000..cbf1fd33 --- /dev/null +++ b/src/Web/Extensions/DecimalExtensions.cs @@ -0,0 +1,16 @@ +namespace PoEGamblingHelper.Web.Extensions; + +public static class DecimalExtensions +{ + public static string Round(this decimal value, int places) + { + var placeCharacters = new string('#', places); + return string.Format($"{{0:0.{placeCharacters}}}", value); + } + + public static string? Round(this decimal? value, int places) + { + var placeCharacters = new string('#', places); + return value is null ? null : string.Format($"{{0:0.{placeCharacters}}}", value); + } +} \ No newline at end of file diff --git a/src/Web/Extensions/HttpContentExtensions.cs b/src/Web/Extensions/HttpContentExtensions.cs new file mode 100644 index 00000000..16b22659 --- /dev/null +++ b/src/Web/Extensions/HttpContentExtensions.cs @@ -0,0 +1,13 @@ +using System.Net.Http.Json; +using PoEGamblingHelper.Application.Exception.Body; + +namespace PoEGamblingHelper.Web.Extensions; + +public static class HttpContentExtensions +{ + public static async Task GetExceptionBody(this HttpContent content) + { + return await content.ReadFromJsonAsync() ?? + new PoeGamblingHelperExceptionBody(ExceptionType.InternalError, ExceptionId.CannotParseBackendException); + } +} \ No newline at end of file diff --git a/src/Web/Extensions/QueryExtensions.cs b/src/Web/Extensions/QueryExtensions.cs new file mode 100644 index 00000000..2a7219c7 --- /dev/null +++ b/src/Web/Extensions/QueryExtensions.cs @@ -0,0 +1,100 @@ +using System.Text.RegularExpressions; +using System.Web; +using PoEGamblingHelper.Application.QueryParameters; +using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Domain.Entity.Gem; + +namespace PoEGamblingHelper.Web.Extensions; + +public static class QueryExtensions +{ + public static string ToQueryString(this GemDataQuery gemDataQuery, PageRequest? page) + { + return page is null + ? gemDataQuery.ToQueryString() + : $"{page.ToQueryString()}{gemDataQuery.ToQueryString(false)}"; + } + + public static string ToQueryString(this GemDataQuery gemDataQuery, bool questionMark = true) + { + var start = questionMark ? "?" : "&"; + var searchText = gemDataQuery.SearchText == string.Empty ? "" : $"&searchText={gemDataQuery.SearchText}"; + var pricePerTryFrom = gemDataQuery.PricePerTryFrom is null + ? "" + : $"&pricePerTryFrom={gemDataQuery.PricePerTryFrom}"; + var pricePerTryTo = gemDataQuery.PricePerTryTo is null ? "" : $"&pricePerTryTo={gemDataQuery.PricePerTryTo}"; + return + $"{start}sort={gemDataQuery.Sort}&gemType={gemDataQuery.GemType}&showAlternateQuality={gemDataQuery.ShowAlternateQuality}&onlyShowProfitable={gemDataQuery.OnlyShowProfitable}&showVaal={gemDataQuery.ShowVaal}{searchText}{pricePerTryFrom}{pricePerTryTo}"; + } + + public static string ToQueryString(this PageRequest pageRequest) + { + return $"?pageNumber={pageRequest.PageNumber}&pageSize={pageRequest.PageSize}"; + } + + public static string TradeUrl(this GemTradeData gemTradeData, + League currentLeague, + bool accurateLevel = true, + bool accurateQuality = false) + { + const string poeTradeUrl = "https://www.pathofexile.com/trade/search"; + const string queryKey = "?q="; + return $"{poeTradeUrl}/{currentLeague.Name}{queryKey}{gemTradeData.TradeQuery(accurateLevel, accurateQuality)}"; + } + + public static string TradeQuery(this GemTradeData gemTradeData, + bool accurateLevel = true, + bool accurateQuality = false) + { + var gemAlternateQuality = -1; + + var firstWord = gemTradeData.Name.Split(" ")[0]; + + var name = gemTradeData.Name; + if (Enum.TryParse(firstWord, true, out AlternateQuality quality)) + { + name = gemTradeData.Name[(firstWord.Length + 1)..]; + gemAlternateQuality = (int)quality + 1; + } + + var corruptedText = $""" + "corrupted": {gemTradeData.Corrupted.ToString().ToLower()} + """; + + var minGemLevel = accurateLevel ? gemTradeData.GemLevel : int.MinValue; + var maxGemLevel = accurateLevel ? gemTradeData.GemLevel : int.MaxValue; + var levelText = !accurateLevel + ? string.Empty + : $$""","gem_level": {"min": {{minGemLevel}},"max": {{maxGemLevel}}}"""; + + var minGemQuality = accurateQuality ? gemTradeData.GemQuality : int.MinValue; + var maxGemQuality = accurateQuality ? gemTradeData.GemQuality : int.MaxValue; + var qualityText = !accurateQuality + ? string.Empty + : $$""","quality": {"min": {{minGemQuality}},"max": {{maxGemQuality}}}"""; + + var gemAlternateQualityText = gemAlternateQuality < 0 + ? string.Empty + : $$""","gem_alternate_quality": {"option": "{{gemAlternateQuality}}"},"""; + + + return Regex.Replace("(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", + $$""" + { + "query": { + "filters": { + "misc_filters": { + "filters": { + {{corruptedText}} + {{levelText}} + {{gemAlternateQualityText}} + {{qualityText}} + } + } + }, + "type": "{{HttpUtility.UrlPathEncode(name)}}" + } + } + """, "$1"); + } +} \ No newline at end of file diff --git a/src/Web/Extensions/StringifyExtensions.cs b/src/Web/Extensions/StringifyExtensions.cs new file mode 100644 index 00000000..c81ff4b7 --- /dev/null +++ b/src/Web/Extensions/StringifyExtensions.cs @@ -0,0 +1,36 @@ +using System.Diagnostics; +using PoEGamblingHelper.Application.QueryParameters; + +namespace PoEGamblingHelper.Web.Extensions; + +public static class StringifyExtensions +{ + public static string Percent(this double value) { return $"{value * 100:0.##}%"; } + + public static string ToPrettyString(this Sort sort) + { + return sort switch + { + Sort.CostPerTryAsc => "Cost Ascending", + Sort.CostPerTryDesc => "Cost Descending", + Sort.AverageProfitPerTryAsc => "Average profit per try Ascending", + Sort.AverageProfitPerTryDesc => "Average profit per try Descending", + Sort.MaxProfitPerTryAsc => "Maximum profit per try Ascending", + Sort.MaxProfitPerTryDesc => "Maximum profit per try Descending", + _ => throw new UnreachableException(nameof(GemType)) + }; + } + + public static string ToPrettyString(this GemType gemType) + { + return gemType switch + { + GemType.All => "All Gems", + GemType.Awakened => "Awakened Support Gem", + GemType.Exceptional => "Exceptional Support Gem", + GemType.Skill => "Skill Gem", + GemType.RegularSupport => "Regular Support Gem", + _ => throw new UnreachableException(nameof(GemType)) + }; + } +} \ No newline at end of file diff --git a/src/Web/Pages/GamblingHelper.razor.cs b/src/Web/Pages/GamblingHelper.razor.cs index 142b069a..ec54b8d0 100644 --- a/src/Web/Pages/GamblingHelper.razor.cs +++ b/src/Web/Pages/GamblingHelper.razor.cs @@ -1,6 +1,7 @@ using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; @@ -43,7 +44,7 @@ protected override async Task OnInitializedAsync() await UpdateService.Update(); } - public async Task LoadGamblingData() + private async Task LoadGamblingData() { try { @@ -55,9 +56,9 @@ public async Task LoadGamblingData() _currency = currency; _filterValues.Currency = _filterValues.Currency is null - ? _currency.First(c => c.Name.Equals("Divine Orb")) + ? _currency.First(c => c.Name.EqualsIgnoreCase("Divine Orb")) : _currency.FirstOrDefault(c => c.Id.Equals(_filterValues.Currency.Id)); - _filterValues.Currency ??= _currency.First(c => c.Name.Equals("Divine Orb")); + _filterValues.Currency ??= _currency.First(c => c.Name.EqualsIgnoreCase("Divine Orb")); var templeCost = await TempleCostService.Get(); @@ -100,6 +101,7 @@ private async Task UpdateGems() var gemPage = await GemService.GetAll(new PageRequest { PageSize = 20, PageNumber = _currentGemPage }, _filterValues.ToQuery()); if (gemPage is null) return false; + _gems.AddRange(gemPage.Content); _currentGemPage = gemPage.CurrentPage; _isOnLastPage = gemPage.LastPage; diff --git a/src/Web/Properties/launchSettings.json b/src/Web/Properties/launchSettings.json index 1fef5336..eaaea0e5 100644 --- a/src/Web/Properties/launchSettings.json +++ b/src/Web/Properties/launchSettings.json @@ -3,7 +3,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "https://localhost:7213;http://localhost:5056", "environmentVariables": { diff --git a/src/Web/Services/Implementations/CurrencyService.cs b/src/Web/Services/Implementations/CurrencyService.cs index 4f241a41..49fbc81d 100644 --- a/src/Web/Services/Implementations/CurrencyService.cs +++ b/src/Web/Services/Implementations/CurrencyService.cs @@ -4,8 +4,8 @@ namespace PoEGamblingHelper.Web.Services.Implementations; -public class CurrencyService : HttpService, ICurrencyService +public class CurrencyService(HttpClient httpClient, IToastService toastService) : HttpService(httpClient, toastService), + ICurrencyService { - public CurrencyService(HttpClient httpClient, IToastService toastService) : base(httpClient, toastService) { } public async Task?> GetAll() { return await GetAsync>("currency"); } } \ No newline at end of file diff --git a/src/Web/Services/Implementations/GemService.cs b/src/Web/Services/Implementations/GemService.cs index 8790fde1..d9a2cddb 100644 --- a/src/Web/Services/Implementations/GemService.cs +++ b/src/Web/Services/Implementations/GemService.cs @@ -1,15 +1,14 @@ using Blazored.Toast.Services; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Web.Extensions; using PoEGamblingHelper.Web.Services.Interfaces; -using PoEGamblingHelper.Web.Util; namespace PoEGamblingHelper.Web.Services.Implementations; -public class GemService : HttpService, IGemService +public class GemService(HttpClient httpClient, IToastService toastService) : HttpService(httpClient, toastService), + IGemService { - public GemService(HttpClient httpClient, IToastService toastService) : base(httpClient, toastService) { } - public async Task?> GetAll(PageRequest? page, GemDataQuery? query) { var queryString = query?.ToQueryString(page) ?? page?.ToQueryString() ?? ""; diff --git a/src/Web/Services/Implementations/LeagueService.cs b/src/Web/Services/Implementations/LeagueService.cs index c1c0afbf..60e82297 100644 --- a/src/Web/Services/Implementations/LeagueService.cs +++ b/src/Web/Services/Implementations/LeagueService.cs @@ -4,8 +4,8 @@ namespace PoEGamblingHelper.Web.Services.Implementations; -public class LeagueService : HttpService, ILeagueService +public class LeagueService(HttpClient httpClient, IToastService toastService) : HttpService(httpClient, toastService), + ILeagueService { - public LeagueService(HttpClient httpClient, IToastService toastService) : base(httpClient, toastService) { } public async Task GetCurrent() { return await GetAsync("league/current"); } } \ No newline at end of file diff --git a/src/Web/Services/Implementations/TempleCostService.cs b/src/Web/Services/Implementations/TempleCostService.cs index 85a5e684..4faee58d 100644 --- a/src/Web/Services/Implementations/TempleCostService.cs +++ b/src/Web/Services/Implementations/TempleCostService.cs @@ -4,8 +4,8 @@ namespace PoEGamblingHelper.Web.Services.Implementations; -public class TempleCostService : HttpService, ITempleCostService +public class TempleCostService + (HttpClient httpClient, IToastService toastService) : HttpService(httpClient, toastService), ITempleCostService { - public TempleCostService(HttpClient httpClient, IToastService toastService) : base(httpClient, toastService) { } public async Task Get() { return await GetAsync("temple"); } } \ No newline at end of file diff --git a/src/Web/Services/Interfaces/HttpService.cs b/src/Web/Services/Interfaces/HttpService.cs index d21d63cc..0b46f521 100644 --- a/src/Web/Services/Interfaces/HttpService.cs +++ b/src/Web/Services/Interfaces/HttpService.cs @@ -1,20 +1,14 @@ using System.Net.Http.Json; using Blazored.Toast.Services; using PoEGamblingHelper.Application.Exception.Body; -using PoEGamblingHelper.Web.Util; +using PoEGamblingHelper.Web.Extensions; namespace PoEGamblingHelper.Web.Services.Interfaces; -public abstract class HttpService +public abstract class HttpService(HttpClient httpClient, IToastService toastService) { - protected HttpService(HttpClient httpClient, IToastService toastService) - { - HttpClient = httpClient; - ToastService = toastService; - } - - protected IToastService ToastService { get; } - protected HttpClient HttpClient { get; } + protected IToastService ToastService { get; } = toastService; + protected HttpClient HttpClient { get; } = httpClient; protected async Task GetAsync(string url) { diff --git a/src/Web/Shared/Components/CurrencyDropDown.razor b/src/Web/Shared/Components/CurrencyDropDown.razor deleted file mode 100644 index 55eff817..00000000 --- a/src/Web/Shared/Components/CurrencyDropDown.razor +++ /dev/null @@ -1,27 +0,0 @@ - - - -@code { - private const string ChaosIcon = "icons/chaos_orb.png"; - private const string ExaltedIcon = "icons/exalted_orb.png"; - private string _selectedCurrency = ChaosIcon; - private void SetSelectedCurrency(string currencyImage) { _selectedCurrency = currencyImage; } -} \ No newline at end of file diff --git a/src/Web/Shared/Components/Filter.razor.cs b/src/Web/Shared/Components/Filter.razor.cs index ccc3a0b2..2ffae129 100644 --- a/src/Web/Shared/Components/Filter.razor.cs +++ b/src/Web/Shared/Components/Filter.razor.cs @@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Components; using PoEGamblingHelper.Application.QueryParameters; using PoEGamblingHelper.Domain.Entity; +using PoEGamblingHelper.Web.Extensions; using PoEGamblingHelper.Web.Shared.Model; -using PoEGamblingHelper.Web.Util; namespace PoEGamblingHelper.Web.Shared.Components; @@ -62,27 +62,28 @@ private string TempleTradeUrl() const string poeTradeUrl = "https://www.pathofexile.com/trade/search"; const string queryKey = "?q="; - var query = JsonMinifyRegex().Replace(@" - { - ""query"":{ - ""stats"":[ - { - ""type"":""and"", - ""filters"":[ - { - ""id"":""pseudo.pseudo_temple_gem_room_3"", - ""value"":{ - ""option"":1 - }, - ""disabled"":false - } - ] - } - ], - ""type"": ""Chronicle of Atzoatl"" - } - } - ", "$1"); + var query = Regex.Replace("(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+", + """ + { + "query":{ + "stats":[ + { + "type":"and", + "filters":[ + { + "id":"pseudo.pseudo_temple_gem_room_3", + "value":{ + "option":1 + }, + "disabled":false + } + ] + } + ], + "type": "Chronicle of Atzoatl" + } + } + """, "$1"); return $"{poeTradeUrl}/{CurrentLeague.Name}{queryKey}{query}"; } @@ -103,8 +104,6 @@ private decimal ConversionRatio() return FilterValues.CurrencyValue ?? FilterValues.Currency?.ChaosEquivalent ?? 1; } - [GeneratedRegex("(\"(?:[^\"\\\\]|\\\\.)*\")|\\s+")] private static partial Regex JsonMinifyRegex(); - #region Update Callback private async Task UpdateTempleCost(ChangeEventArgs args) diff --git a/src/Web/Shared/Components/GemStats.razor.cs b/src/Web/Shared/Components/GemStats.razor.cs index c7603a4d..36f9d9a8 100644 --- a/src/Web/Shared/Components/GemStats.razor.cs +++ b/src/Web/Shared/Components/GemStats.razor.cs @@ -4,8 +4,8 @@ using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity; using PoEGamblingHelper.Domain.Entity.Gem; +using PoEGamblingHelper.Web.Extensions; using PoEGamblingHelper.Web.Shared.Model; -using PoEGamblingHelper.Web.Util; namespace PoEGamblingHelper.Web.Shared.Components; diff --git a/src/Web/Shared/Components/Navbar.razor b/src/Web/Shared/Components/Navbar.razor index 33429d23..bd483489 100644 --- a/src/Web/Shared/Components/Navbar.razor +++ b/src/Web/Shared/Components/Navbar.razor @@ -1,4 +1,4 @@ -
+
+ + +
diff --git a/src/Web/Pages/Analytics/Analytics.razor.cs b/src/Web/Pages/Analytics/Analytics.razor.cs index 278af6fb..65045059 100644 --- a/src/Web/Pages/Analytics/Analytics.razor.cs +++ b/src/Web/Pages/Analytics/Analytics.razor.cs @@ -15,97 +15,83 @@ public enum RangeSelect } private ChartData _chartData = null!; + private DateTime _endDate = DateTime.UtcNow; private LineChart _lineChart = null!; + private bool _lineChartInitialized; private LineChartOptions _lineChartOptions = null!; private RangeSelect _selectedRange = RangeSelect.Last30Days; + private DateTime _startDate = DateTime.UtcNow.AddDays(-30); [Inject] private IAnalyticsService AnalyticsService { get; set; } = null!; + [Inject] private ILeagueService LeagueService { get; set; } = null!; - protected override async Task OnInitializedAsync() + public DateTime StartDate { - var analytics = await AnalyticsService.GetAll(); - if (analytics is null) throw new Exception("Analytics not found"); - - var last30Days = new DateTime[30]; - for (var i = 0; i < last30Days.Length; i++) last30Days[^(i + 1)] = DateTime.Today.AddDays(-i); - - - var colors = ColorUtility.CategoricalTwelveColors; - - var labels = last30Days.Select(d => $"{d.Day}.{d.Month}.{d.Year}").ToList(); - - var dataset = new LineChartDataset - { - Label = "Views per Day", - Data = analytics.Take(30).Select(a => (double?)a.Views).ToList(), - BackgroundColor = colors[0], - BorderColor = colors[0], - BorderWidth = 2, - HoverBorderWidth = 4, - PointBackgroundColor = [colors[0]], - PointRadius = [0], - PointHoverRadius = [4] - }; - - _chartData = new ChartData - { - Labels = labels, - Datasets = [dataset] - }; - - _lineChartOptions = new LineChartOptions + get => _startDate; + set { - Responsive = true, - Interaction = new Interaction { Mode = InteractionMode.Index } - }; - - await _lineChart.InitializeAsync(_chartData, _lineChartOptions); + _startDate = value; + Console.WriteLine(2); + if (_selectedRange == RangeSelect.Custom) _ = UpdateSelectedRange(_selectedRange); + } } - protected override async Task OnAfterRenderAsync(bool firstRender) + public DateTime EndDate { - // if (firstRender) await _lineChart.InitializeAsync(_chartData, _lineChartOptions); - await base.OnAfterRenderAsync(firstRender); + get => _endDate; + set + { + _endDate = value; + Console.WriteLine(1); + if (_selectedRange == RangeSelect.Custom) _ = UpdateSelectedRange(_selectedRange); + } } private async Task UpdateSelectedRange(RangeSelect range) { + Console.WriteLine(3); _selectedRange = range; + DateTime start; + var end = DateTime.UtcNow; + switch (range) { case RangeSelect.Last30Days: - Console.WriteLine(1); - await CreateLineChart(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow); + start = DateTime.UtcNow.AddDays(-30); break; case RangeSelect.LastYear: - await CreateLineChart(DateTime.UtcNow.AddYears(-1), DateTime.UtcNow); + start = DateTime.UtcNow.AddYears(-1); break; case RangeSelect.CurrentLeague: - //TODO - await CreateLineChart(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow); + var league = await LeagueService.GetCurrent(); + if (league is null) throw new Exception("League not found"); + start = league.StartDate; break; case RangeSelect.Custom: - //TODO - await CreateLineChart(DateTime.UtcNow.AddDays(-2), DateTime.UtcNow); + if (_startDate > _endDate) return; + start = _startDate; + end = _endDate; break; default: throw new ArgumentOutOfRangeException(nameof(range), range, null); } - } - private async Task CreateLineChart(DateTime start, DateTime end) - { var analytics = await AnalyticsService.Get(start, end); if (analytics is null) throw new Exception("Analytics not found"); + var data = analytics.Select(a => new Data($"{a.Date.Day}.{a.Date.Month}.{a.Date.Year}", a.Views)).ToList(); + await CreateLineChart(data); + } + private async Task CreateLineChart(List data) + { var colors = ColorUtility.CategoricalTwelveColors; - var labels = analytics.Select(a => $"{a.Date.Day}.{a.Date.Month}.{a.Date.Year}").ToList(); + var labels = data.Select(d => d.Label).ToList(); var dataset = new LineChartDataset { Label = "Views per Day", - Data = analytics.Take(30).Select(a => (double?)a.Views).ToList(), + Data = data.Select(d => (double?)d.Value).ToList(), BackgroundColor = colors[0], BorderColor = colors[0], BorderWidth = 2, @@ -127,6 +113,22 @@ private async Task CreateLineChart(DateTime start, DateTime end) Interaction = new Interaction { Mode = InteractionMode.Index } }; - await _lineChart.InitializeAsync(_chartData, _lineChartOptions); + if (_lineChartInitialized) + { + await _lineChart.UpdateAsync(_chartData, _lineChartOptions); + } + else + { + await _lineChart.InitializeAsync(_chartData, _lineChartOptions); + _lineChartInitialized = true; + } + } + + protected override async Task OnInitializedAsync() + { + await UpdateSelectedRange(_selectedRange); + await base.OnInitializedAsync(); } + + private record Data(string Label, double Value); } \ No newline at end of file From 09a89de00f61ffe7129a4b07fa7588cea2f1368f Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 3 Jul 2025 23:29:26 +0200 Subject: [PATCH 098/129] cleanup --- src/Web/Pages/Analytics/Analytics.razor.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Web/Pages/Analytics/Analytics.razor.cs b/src/Web/Pages/Analytics/Analytics.razor.cs index 65045059..6330ae51 100644 --- a/src/Web/Pages/Analytics/Analytics.razor.cs +++ b/src/Web/Pages/Analytics/Analytics.razor.cs @@ -31,7 +31,7 @@ public DateTime StartDate set { _startDate = value; - Console.WriteLine(2); + Console.WriteLne(2); if (_selectedRange == RangeSelect.Custom) _ = UpdateSelectedRange(_selectedRange); } } @@ -42,14 +42,12 @@ public DateTime EndDate set { _endDate = value; - Console.WriteLine(1); if (_selectedRange == RangeSelect.Custom) _ = UpdateSelectedRange(_selectedRange); } } private async Task UpdateSelectedRange(RangeSelect range) { - Console.WriteLine(3); _selectedRange = range; DateTime start; var end = DateTime.UtcNow; From 7c8ca2c80a0eb58db9198b19e7d5630479e3f5b9 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Thu, 3 Jul 2025 23:29:37 +0200 Subject: [PATCH 099/129] wat --- src/Web/Pages/Analytics/Analytics.razor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Web/Pages/Analytics/Analytics.razor.cs b/src/Web/Pages/Analytics/Analytics.razor.cs index 6330ae51..3950b5b3 100644 --- a/src/Web/Pages/Analytics/Analytics.razor.cs +++ b/src/Web/Pages/Analytics/Analytics.razor.cs @@ -31,7 +31,6 @@ public DateTime StartDate set { _startDate = value; - Console.WriteLne(2); if (_selectedRange == RangeSelect.Custom) _ = UpdateSelectedRange(_selectedRange); } } From 48c923223f6c138ac7d7a6867556bf991298c2bc Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 00:54:57 +0200 Subject: [PATCH 100/129] add ApiKey for Analytics --- src/Api/Controllers/AnalyticsController.cs | 58 ++++++++++++++++- src/Api/Program.cs | 32 +++++++++- src/Api/appsettings.Development.json | 6 +- src/Api/appsettings.json | 2 +- src/Web/Pages/Analytics/Analytics.razor | 62 +++++++++---------- src/Web/Pages/Analytics/Analytics.razor.cs | 8 ++- .../Implementations/AnalyticsService.cs | 10 +++ src/Web/Services/Interfaces/HttpService.cs | 57 ++++++++++++++++- .../Services/Interfaces/IAnalyticsService.cs | 6 +- .../Shared/Components/PassKeyProtection.razor | 42 +++++++++++-- 10 files changed, 236 insertions(+), 47 deletions(-) diff --git a/src/Api/Controllers/AnalyticsController.cs b/src/Api/Controllers/AnalyticsController.cs index e43291b7..8014685b 100644 --- a/src/Api/Controllers/AnalyticsController.cs +++ b/src/Api/Controllers/AnalyticsController.cs @@ -1,10 +1,14 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PoEGamblingHelper.Application.Repositories; using PoEGamblingHelper.Domain.Entity.Analytics; namespace PoEGamblingHelper.Api.Controllers; -public class AnalyticsController(IAnalyticsDayRepository analyticsRepository) : ApiControllerBase +public class AnalyticsController(IAnalyticsDayRepository analyticsRepository, IConfiguration configuration) : ApiControllerBase { [HttpGet] public IAsyncEnumerable Get([FromQuery] DateOnly? start, [FromQuery] DateOnly? end) @@ -13,4 +17,56 @@ public IAsyncEnumerable Get([FromQuery] DateOnly? start, [FromQuer return analyticsRepository.Get(start.Value, end.Value); } + + [HttpGet("check")] + [Authorize] + public async Task Check() + { + var claims = new List + { + new(ClaimTypes.Name, "test") + }; + var claimsIdentity = new ClaimsIdentity( + claims, + CookieAuthenticationDefaults.AuthenticationScheme); + + var authProperties = new AuthenticationProperties + { + IsPersistent = true, + ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30) + }; + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), + authProperties); + + return true; + } + + [HttpGet("login")] + public async Task Login([FromHeader(Name = "Authorization")] string apiKey) + { + var apiKeySolution = configuration.GetValue("ApiKey"); + Console.WriteLine(apiKey); + Console.WriteLine(apiKeySolution); + if (apiKey.Length <= "Bearer ".Length || !string.Equals(apiKey["Bearer ".Length..], apiKeySolution)) return Unauthorized(); + + var claimsIdentity = new ClaimsIdentity( + [new Claim(ClaimTypes.Name, "ApiKeyUser")], + CookieAuthenticationDefaults.AuthenticationScheme); + + var authProperties = new AuthenticationProperties + { + IsPersistent = true, + ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30) + }; + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), + authProperties); + + return Ok(); + } } \ No newline at end of file diff --git a/src/Api/Program.cs b/src/Api/Program.cs index 25022661..4b6e81ae 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Microsoft.AspNetCore.Authentication.Cookies; using PoEGamblingHelper.Api.Configuration; using PoEGamblingHelper.Api.Filters; using PoEGamblingHelper.Api.Middleware; @@ -22,6 +23,30 @@ builder.Services.AddInfrastructureServices(builder.Configuration); +builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.Cookie.Name = "AnalyticsCookie"; + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.ExpireTimeSpan = TimeSpan.FromDays(30); + options.SlidingExpiration = true; + options.LoginPath = "/analytics/login"; + + options.Events.OnRedirectToLogin = ctx => + { + ctx.Response.StatusCode = 401; + return Task.CompletedTask; + }; + + options.Events.OnRedirectToAccessDenied = ctx => + { + ctx.Response.StatusCode = 403; + return Task.CompletedTask; + }; + }); +builder.Services.AddAuthorization(); + var app = builder.Build(); if (app.Environment.IsDevelopment()) @@ -32,14 +57,17 @@ app.UseHttpsRedirection(); -app.UseCors(corsBuilder => corsBuilder.WithOrigins(app.Configuration["AllowedOrigins"]!) +app.UseCors(corsBuilder => corsBuilder.WithOrigins(app.Configuration.GetSection("AllowedOrigins").Get()!) .AllowAnyMethod() - .AllowAnyHeader()); + .AllowAnyHeader() + .AllowCredentials()); app.UseRateLimiter(); app.UseOutputCache(); app.MapControllers(); app.MigrateDatabase(); app.UseAnalytics(); +app.UseAuthentication(); +app.UseAuthorization(); app.Run(); \ No newline at end of file diff --git a/src/Api/appsettings.Development.json b/src/Api/appsettings.Development.json index d5ca92df..f0c7db3a 100644 --- a/src/Api/appsettings.Development.json +++ b/src/Api/appsettings.Development.json @@ -3,5 +3,9 @@ "DBConnection": "User ID=postgres;Server=localhost;Port=5432;Database=PoeGamblingHelper;Pooling=true;" }, "POSTGRES_PASSWORD": "1", - "AllowedOrigins": "*" + "AllowedOrigins": [ + "https://localhost:7213", + "http://localhost:5056" + ], + "ApiKey": "1" } diff --git a/src/Api/appsettings.json b/src/Api/appsettings.json index 88337543..c857e814 100644 --- a/src/Api/appsettings.json +++ b/src/Api/appsettings.json @@ -11,7 +11,7 @@ } }, "AllowedHosts": "*", - "AllowedOrigins": "", + "AllowedOrigins": [], "CacheTag": "FetchData", "FetchInterval": "5", "Quartz": { diff --git a/src/Web/Pages/Analytics/Analytics.razor b/src/Web/Pages/Analytics/Analytics.razor index 7019979e..d2179996 100644 --- a/src/Web/Pages/Analytics/Analytics.razor +++ b/src/Web/Pages/Analytics/Analytics.razor @@ -1,36 +1,36 @@ @page "/analytics" - - -
-
- -} - - -@code { - [Parameter] public RenderFragment? ChildContent { get; set; } - [Inject] private IAnalyticsService AnalyticsService { get; set; } = null!; - private string _apiKey = ""; - private bool _isLoggedIn; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - - _isLoggedIn = await AnalyticsService.Check(); - } - - private async Task Login() - { - await AnalyticsService.Login(_apiKey); - _isLoggedIn = await AnalyticsService.Check(); - } - - private async Task Submit(KeyboardEventArgs arg) - { - if (arg.Key == "Enter") await Login(); - } - } \ No newline at end of file diff --git a/src/Web/Shared/Components/PassKeyProtection.razor.cs b/src/Web/Shared/Components/PassKeyProtection.razor.cs new file mode 100644 index 00000000..f307fa2b --- /dev/null +++ b/src/Web/Shared/Components/PassKeyProtection.razor.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using PoEGamblingHelper.Web.Services.Interfaces; + +namespace PoEGamblingHelper.Web.Shared.Components; + +public partial class PassKeyProtection +{ + private string _apiKey = ""; + private bool _isLoggedIn; + [Parameter] public RenderFragment? ChildContent { get; set; } + [Inject] private IAnalyticsService AnalyticsService { get; set; } = null!; + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + _isLoggedIn = await AnalyticsService.Check(); + } + + private async Task Login() + { + await AnalyticsService.Login(_apiKey); + _isLoggedIn = await AnalyticsService.Check(); + } + + private async Task Submit(KeyboardEventArgs arg) + { + if (arg.Key == "Enter") await Login(); + } +} \ No newline at end of file From 4d10a6f4ecfc47888f6b03fa1bdc591672052bae Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 11:24:08 +0200 Subject: [PATCH 105/129] add Calculator --- .../Pages/GamblingHelper/GamblingHelper.razor | 1 + src/Web/Shared/Components/MathInput.razor | 5 +++ src/Web/Shared/Components/MathInput.razor.cs | 35 +++++++++++++++++++ src/Web/Shared/Components/MathInput.razor.css | 1 + 4 files changed, 42 insertions(+) create mode 100644 src/Web/Shared/Components/MathInput.razor create mode 100644 src/Web/Shared/Components/MathInput.razor.cs create mode 100644 src/Web/Shared/Components/MathInput.razor.css diff --git a/src/Web/Pages/GamblingHelper/GamblingHelper.razor b/src/Web/Pages/GamblingHelper/GamblingHelper.razor index 36443499..78a7fb96 100644 --- a/src/Web/Pages/GamblingHelper/GamblingHelper.razor +++ b/src/Web/Pages/GamblingHelper/GamblingHelper.razor @@ -5,6 +5,7 @@ Gambling Helper +
icon
diff --git a/src/Web/Shared/Components/MathInput.razor b/src/Web/Shared/Components/MathInput.razor new file mode 100644 index 00000000..0dbe81cb --- /dev/null +++ b/src/Web/Shared/Components/MathInput.razor @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/src/Web/Shared/Components/MathInput.razor.cs b/src/Web/Shared/Components/MathInput.razor.cs new file mode 100644 index 00000000..af57b585 --- /dev/null +++ b/src/Web/Shared/Components/MathInput.razor.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Components; + +namespace PoEGamblingHelper.Web.Shared.Components; + +public partial class MathInput : ComponentBase +{ + public string Value { get; set; } + + protected override async Task OnInitializedAsync() + { + Console.WriteLine(Calc("123.123 + 231232.32 / 132123 * 1234 * 432 - 123 * 123 + 13 / 54")); + } + + private void Calculate(ChangeEventArgs obj) + { + var multiplications = Value.Split('*'); + } + + private static double Calc(string expression) + { + var addition = expression.Split('+'); + if (addition.Length > 1) return addition.Aggregate(0.0, (acc, val) => acc + Calc(val)); + + var subtractions = expression.Split('-'); + if (subtractions.Length > 1) return subtractions.Skip(1).Aggregate(Calc(subtractions[0]), (acc, val) => acc - Calc(val)); + + var multiplications = expression.Split('*'); + if (multiplications.Length > 1) return multiplications.Aggregate(1.0, (acc, val) => acc * Calc(val)); + + var division = expression.Split('/'); + if (division.Length > 1) return division.Skip(1).Aggregate(Calc(division[0]), (acc, val) => acc / Calc(val)); + + return double.Parse(expression); + } +} \ No newline at end of file diff --git a/src/Web/Shared/Components/MathInput.razor.css b/src/Web/Shared/Components/MathInput.razor.css new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/src/Web/Shared/Components/MathInput.razor.css @@ -0,0 +1 @@ + \ No newline at end of file From 5a4ef6df82ee4d21c1a09de9bc83a0b5f4b7317f Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 20:44:43 +0200 Subject: [PATCH 106/129] finish MathInput --- src/Web/Shared/Components/MathInput.razor | 3 ++- src/Web/Shared/Components/MathInput.razor.cs | 21 ++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Web/Shared/Components/MathInput.razor b/src/Web/Shared/Components/MathInput.razor index 0dbe81cb..9f18c1ac 100644 --- a/src/Web/Shared/Components/MathInput.razor +++ b/src/Web/Shared/Components/MathInput.razor @@ -2,4 +2,5 @@ pattern="([0-9]+(\.[0-9]+)? ?[\+\-\*\/]? ?)+" class="form-control" value="@Value" - @onchange="Calculate"> \ No newline at end of file + @attributes="InputAttributes" + @onchange="OnValueChanged"> \ No newline at end of file diff --git a/src/Web/Shared/Components/MathInput.razor.cs b/src/Web/Shared/Components/MathInput.razor.cs index af57b585..82b10829 100644 --- a/src/Web/Shared/Components/MathInput.razor.cs +++ b/src/Web/Shared/Components/MathInput.razor.cs @@ -1,19 +1,21 @@ -using Microsoft.AspNetCore.Components; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Components; namespace PoEGamblingHelper.Web.Shared.Components; public partial class MathInput : ComponentBase { - public string Value { get; set; } + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary? InputAttributes { get; set; } - protected override async Task OnInitializedAsync() - { - Console.WriteLine(Calc("123.123 + 231232.32 / 132123 * 1234 * 432 - 123 * 123 + 13 / 54")); - } + public double Value { get; set; } - private void Calculate(ChangeEventArgs obj) + private void OnValueChanged(ChangeEventArgs obj) { - var multiplications = Value.Split('*'); + if (obj.Value is null) return; + var value = obj.Value.ToString()!.Trim(); + if (!InputRegex().IsMatch(value)) return; + Value = Calc(value); } private static double Calc(string expression) @@ -32,4 +34,7 @@ private static double Calc(string expression) return double.Parse(expression); } + + [GeneratedRegex(@"^([0-9]+(\.[0-9]+)? ?[\+\-\*\/]? ?)+$")] + private static partial Regex InputRegex(); } \ No newline at end of file From 67eb02d009bb81468c234f9d379d0c5d3e5f2c09 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 22:44:06 +0200 Subject: [PATCH 107/129] this somehow works man --- .../GamblingHelper/Components/GemStats.razor | 42 +++++++++--- .../Components/GemStats.razor.cs | 67 ++++++++++++------- .../Components/GemStats.razor.css | 4 -- .../Pages/GamblingHelper/GamblingHelper.razor | 3 +- src/Web/Shared/Components/MathInput.razor.cs | 20 +++--- 5 files changed, 87 insertions(+), 49 deletions(-) diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor b/src/Web/Pages/GamblingHelper/Components/GemStats.razor index 8127e0cb..fff9b085 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor @@ -26,12 +26,17 @@ {
Price Raw: - + @* *@ +
} else { - Price Raw: @CurrencyValue(_values.RawValue ?? GemData.RawCost()) + Price + Raw: @CurrencyValue(_values.RawValue is null ? GemData.RawCost() : decimal.Parse(_values.RawValue)) }

@@ -39,7 +44,12 @@ {

Worst Case: - +
} else @@ -52,7 +62,11 @@ {
Middle Case: - +
} else @@ -65,7 +79,11 @@ {
Best Case: - +
} else @@ -76,10 +94,14 @@
Profit per Try
-

Cost: @CurrencyValue(GemData.CostPerTry(_values.RawValue, FilterTempleCost()))

-

Worst Case: @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, _values.RawValue, FilterTempleCost()) : GemData.Profit((decimal)_values.WorstCaseValue, _values.RawValue, FilterTempleCost()))

-

Middle Case: @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, _values.RawValue, FilterTempleCost()) : GemData.Profit((decimal)_values.MiddleCaseValue, _values.RawValue, FilterTempleCost()))

-

Best Case: @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, _values.RawValue, FilterTempleCost()) : GemData.Profit((decimal)_values.BestCaseValue, _values.RawValue, FilterTempleCost()))

+

+ Cost: @CurrencyValue(GemData.CostPerTry(_values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

+

Worst + Case: @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.WorstCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

+

Middle + Case: @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.MiddleCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

+

Best + Case: @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.BestCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

@@ -89,7 +111,7 @@
Average Profit per Try
diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs index 9993285a..322743fb 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs @@ -1,4 +1,5 @@ -using Blazored.LocalStorage; +using System.Globalization; +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity; @@ -21,19 +22,22 @@ public partial class GemStats [Parameter] public League CurrentLeague { get; set; } = null!; [Inject] private ILocalStorageService LocalStorage { get; set; } = default!; - private decimal FilterTempleCost() { return FilterModel.TempleCost ?? TempleCost.AverageChaosValue(); } + private decimal FilterTempleCost() + { + return FilterModel.TempleCost ?? TempleCost.AverageChaosValue(); + } private string TradeUrl(GemData gemData, ResultCase? resultCase = null) { if (resultCase is null) // raw gem { var rawUrlGem = new GemTradeData - { - Name = gemData.Name, - Corrupted = false, - GemLevel = gemData.MaxLevel(), - GemQuality = 0 - }; + { + Name = gemData.Name, + Corrupted = false, + GemLevel = gemData.MaxLevel(), + GemQuality = 0 + }; return rawUrlGem.TradeUrl(CurrentLeague); } @@ -44,35 +48,42 @@ private string TradeUrl(GemData gemData, ResultCase? resultCase = null) if (resultGem is not null) return resultGem.TradeUrl(CurrentLeague); var urlGem = new GemTradeData - { - Name = gemData.Name, - Corrupted = true, - GemLevel = gemData.MaxLevel() + resultCase.Value.LevelModifier(), - GemQuality = 0 - }; + { + Name = gemData.Name, + Corrupted = true, + GemLevel = gemData.MaxLevel() + resultCase.Value.LevelModifier(), + GemQuality = 0 + }; return urlGem.TradeUrl(CurrentLeague); } - private string CurrencyValue(decimal chaosValue) { return (chaosValue / CurrencyValue()).Round(2); } + private string CurrencyValue(decimal chaosValue) + { + return (chaosValue / CurrencyValue()).Round(2); + } private decimal CurrencyValue() { - return FilterModel.CurrencyValue - ?? FilterModel.Currency?.ChaosEquivalent - ?? 1; + return FilterModel.CurrencyValue ?? FilterModel.Currency?.ChaosEquivalent ?? 1; } - private async Task UpdateRawValue(ChangeEventArgs args) + private async Task UpdateRawValue(string? newValue) { - if (args.Value is null || string.IsNullOrWhiteSpace(args.Value.ToString())) + Console.WriteLine(1); + if (string.IsNullOrWhiteSpace(newValue)) { _values.RawValue = null; await SaveValues(); return; } - if (!decimal.TryParse(args.Value.ToString(), out var value)) return; - _values.RawValue = value * CurrencyValue(); + Console.WriteLine(2); + if (!decimal.TryParse(newValue, out var value)) return; + Console.WriteLine(_values.RawValue); + Console.WriteLine(3); + _values.RawValue = (value * CurrencyValue()).ToString(CultureInfo.InvariantCulture); + Console.WriteLine(4); + Console.WriteLine(_values.RawValue); await SaveValues(); } @@ -125,7 +136,10 @@ private async Task ResetValues() _isEditing = false; } - private async Task SaveValues() { await LocalStorage.SetItemAsync(GemData.Id.ToString(), _values); } + private async Task SaveValues() + { + await LocalStorage.SetItemAsync(GemData.Id.ToString(), _values); + } protected override async Task OnInitializedAsync() { @@ -133,11 +147,14 @@ protected override async Task OnInitializedAsync() _values = values ?? new Values(); } - private string GetCurrencyString(decimal? value) { return value is null ? "" : CurrencyValue(value.Value); } + private string GetCurrencyString(decimal? value) + { + return value is null ? "" : CurrencyValue(value.Value); + } private class Values { - public decimal? RawValue { get; set; } + public string? RawValue { get; set; } public decimal? WorstCaseValue { get; set; } public decimal? MiddleCaseValue { get; set; } public decimal? BestCaseValue { get; set; } diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.css b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.css index 83ad658f..ba0cd4c6 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.css +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.css @@ -34,10 +34,6 @@ mask: url(icons/arrow-rotate-right.svg) no-repeat center; } -input { - padding-right: 8px !important; -} - .card { border: none; background-color: var(--bs-body-bg-accent); diff --git a/src/Web/Pages/GamblingHelper/GamblingHelper.razor b/src/Web/Pages/GamblingHelper/GamblingHelper.razor index 78a7fb96..28c56094 100644 --- a/src/Web/Pages/GamblingHelper/GamblingHelper.razor +++ b/src/Web/Pages/GamblingHelper/GamblingHelper.razor @@ -3,9 +3,8 @@ @using PoEGamblingHelper.Web.Pages.GamblingHelper.Components.Filter @using PoEGamblingHelper.Web.Shared.Layout.Components -Gambling Helper +PoE Gambling Helper -
icon
diff --git a/src/Web/Shared/Components/MathInput.razor.cs b/src/Web/Shared/Components/MathInput.razor.cs index 82b10829..5ebf7c1e 100644 --- a/src/Web/Shared/Components/MathInput.razor.cs +++ b/src/Web/Shared/Components/MathInput.razor.cs @@ -1,4 +1,5 @@ -using System.Text.RegularExpressions; +using System.Globalization; +using System.Text.RegularExpressions; using Microsoft.AspNetCore.Components; namespace PoEGamblingHelper.Web.Shared.Components; @@ -8,31 +9,34 @@ public partial class MathInput : ComponentBase [Parameter(CaptureUnmatchedValues = true)] public Dictionary? InputAttributes { get; set; } - public double Value { get; set; } + [Parameter] public string? Value { get; set; } - private void OnValueChanged(ChangeEventArgs obj) + [Parameter] public EventCallback ValueChanged { get; set; } + + private async Task OnValueChanged(ChangeEventArgs obj) { if (obj.Value is null) return; var value = obj.Value.ToString()!.Trim(); if (!InputRegex().IsMatch(value)) return; - Value = Calc(value); + Value = Calc(value).ToString(CultureInfo.InvariantCulture); + await ValueChanged.InvokeAsync(Value); } - private static double Calc(string expression) + private static decimal Calc(string expression) { var addition = expression.Split('+'); - if (addition.Length > 1) return addition.Aggregate(0.0, (acc, val) => acc + Calc(val)); + if (addition.Length > 1) return addition.Aggregate(0m, (acc, val) => acc + Calc(val)); var subtractions = expression.Split('-'); if (subtractions.Length > 1) return subtractions.Skip(1).Aggregate(Calc(subtractions[0]), (acc, val) => acc - Calc(val)); var multiplications = expression.Split('*'); - if (multiplications.Length > 1) return multiplications.Aggregate(1.0, (acc, val) => acc * Calc(val)); + if (multiplications.Length > 1) return multiplications.Aggregate(1m, (acc, val) => acc * Calc(val)); var division = expression.Split('/'); if (division.Length > 1) return division.Skip(1).Aggregate(Calc(division[0]), (acc, val) => acc / Calc(val)); - return double.Parse(expression); + return decimal.Parse(expression); } [GeneratedRegex(@"^([0-9]+(\.[0-9]+)? ?[\+\-\*\/]? ?)+$")] From c2f0d246c2874793819399b6653f6521105ac734 Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 23:10:27 +0200 Subject: [PATCH 108/129] use MathInput in GemStats --- .../GamblingHelper/Components/GemStats.razor | 121 +++++++++--------- .../Components/GemStats.razor.cs | 60 +++++---- src/Web/Shared/Components/MathInput.razor | 1 - 3 files changed, 94 insertions(+), 88 deletions(-) diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor b/src/Web/Pages/GamblingHelper/Components/GemStats.razor index fff9b085..4ffe5a4e 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor @@ -24,94 +24,95 @@

@if (_isEditing) { -

- Price Raw: - @* *@ - -
- } - else - { +
+ Price Raw: + +
+ } + else + { Price - Raw: @CurrencyValue(_values.RawValue is null ? GemData.RawCost() : decimal.Parse(_values.RawValue)) - } + Raw: @CurrencyValue(ToDecimal(_values.RawValue) ?? GemData.RawCost()) + }

@if (_isEditing) { -

- Worst Case: - + Worst Case: + -
- } - else - { - Worst Case: @CurrencyValue(_values.WorstCaseValue ?? GemData.Value(ResultCase.Worst)) - } + ValueChanged="UpdateWorstCaseValue"/> +
+ } + else + { + Worst + Case: @CurrencyValue(ToDecimal(_values.WorstCaseValue) ?? GemData.Value(ResultCase.Worst)) + }

@if (_isEditing) { -

- Middle Case: - + Middle Case: + -
- } - else - { - Middle Case: @CurrencyValue(_values.MiddleCaseValue ?? GemData.Value(ResultCase.Middle)) - } + ValueChanged="UpdateMiddleCaseValue"/> +
+ } + else + { + Middle + Case: @CurrencyValue(ToDecimal(_values.MiddleCaseValue) ?? GemData.Value(ResultCase.Middle)) + }

@if (_isEditing) { -

- Best Case: - -
- } - else - { - Best Case: @CurrencyValue(_values.BestCaseValue ?? GemData.Value(ResultCase.Best)) - } +
+ Best Case: + +
+ } + else + { + Best + Case: @CurrencyValue(ToDecimal(_values.BestCaseValue) ?? GemData.Value(ResultCase.Best)) + }

Profit per Try

- Cost: @CurrencyValue(GemData.CostPerTry(_values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

-

Worst - Case: @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.WorstCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

-

Middle - Case: @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.MiddleCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

-

Best - Case: @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()) : GemData.Profit((decimal)_values.BestCaseValue, _values.RawValue is null ? null : decimal.Parse(_values.RawValue), FilterTempleCost()))

+ Cost: @CurrencyValue(GemData.CostPerTry(ToDecimal(_values.RawValue), FilterTempleCost()))

+

+ Worst Case: + @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.WorstCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) +

+

+ Middle Case: + @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.MiddleCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) +

+

+ Best Case: + @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.BestCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) +

-
+
Average Profit per Try
diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs index 322743fb..04ec1dd6 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using Blazored.LocalStorage; +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using PoEGamblingHelper.Application.Extensions; using PoEGamblingHelper.Domain.Entity; @@ -69,7 +68,6 @@ private decimal CurrencyValue() private async Task UpdateRawValue(string? newValue) { - Console.WriteLine(1); if (string.IsNullOrWhiteSpace(newValue)) { _values.RawValue = null; @@ -77,55 +75,54 @@ private async Task UpdateRawValue(string? newValue) return; } - Console.WriteLine(2); - if (!decimal.TryParse(newValue, out var value)) return; - Console.WriteLine(_values.RawValue); - Console.WriteLine(3); - _values.RawValue = (value * CurrencyValue()).ToString(CultureInfo.InvariantCulture); - Console.WriteLine(4); - Console.WriteLine(_values.RawValue); + var value = ToDecimal(newValue); + if (value is null) return; + _values.RawValue = (value * CurrencyValue()).ToString(); await SaveValues(); } - private async Task UpdateWorstCaseValue(ChangeEventArgs args) + private async Task UpdateWorstCaseValue(string? newValue) { - if (args.Value is null || string.IsNullOrWhiteSpace(args.Value.ToString())) + if (string.IsNullOrWhiteSpace(newValue)) { _values.WorstCaseValue = null; await SaveValues(); return; } - if (!decimal.TryParse(args.Value.ToString(), out var value)) return; - _values.WorstCaseValue = value * CurrencyValue(); + var value = ToDecimal(newValue); + if (value is null) return; + _values.WorstCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); } - private async Task UpdateMiddleCaseValue(ChangeEventArgs args) + private async Task UpdateMiddleCaseValue(string? newValue) { - if (args.Value is null || string.IsNullOrWhiteSpace(args.Value.ToString())) + if (string.IsNullOrWhiteSpace(newValue)) { _values.MiddleCaseValue = null; await SaveValues(); return; } - if (!decimal.TryParse(args.Value.ToString(), out var value)) return; - _values.MiddleCaseValue = value * CurrencyValue(); + var value = ToDecimal(newValue); + if (value is null) return; + _values.MiddleCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); } - private async Task UpdateBestCaseValue(ChangeEventArgs args) + private async Task UpdateBestCaseValue(string? newValue) { - if (args.Value is null || string.IsNullOrWhiteSpace(args.Value.ToString())) + if (string.IsNullOrWhiteSpace(newValue)) { _values.BestCaseValue = null; await SaveValues(); return; } - if (!decimal.TryParse(args.Value.ToString(), out var value)) return; - _values.BestCaseValue = value * CurrencyValue(); + var value = ToDecimal(newValue); + if (value is null) return; + _values.BestCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); } @@ -147,16 +144,25 @@ protected override async Task OnInitializedAsync() _values = values ?? new Values(); } - private string GetCurrencyString(decimal? value) + private string GetCurrencyString(string? value) + { + var parsed = ToDecimal(value); + return parsed is null ? "" : CurrencyValue(parsed.Value); + } + + private static decimal? ToDecimal(string? value) { - return value is null ? "" : CurrencyValue(value.Value); + if (value is null) return null; + var couldBeParsed = decimal.TryParse(value, out var parsed); + if (!couldBeParsed) return null; + return parsed; } private class Values { public string? RawValue { get; set; } - public decimal? WorstCaseValue { get; set; } - public decimal? MiddleCaseValue { get; set; } - public decimal? BestCaseValue { get; set; } + public string? WorstCaseValue { get; set; } + public string? MiddleCaseValue { get; set; } + public string? BestCaseValue { get; set; } } } \ No newline at end of file diff --git a/src/Web/Shared/Components/MathInput.razor b/src/Web/Shared/Components/MathInput.razor index 9f18c1ac..27b5121a 100644 --- a/src/Web/Shared/Components/MathInput.razor +++ b/src/Web/Shared/Components/MathInput.razor @@ -1,6 +1,5 @@  \ No newline at end of file From bdc2705d1e6e8bfa746480ed7feddaba7875c89b Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 23:14:07 +0200 Subject: [PATCH 109/129] cleanup --- src/Web/Extensions/DecimalExtensions.cs | 8 +++++ .../GamblingHelper/Components/GemStats.razor | 35 ++++++++++++------- .../Components/GemStats.razor.cs | 18 +++------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/Web/Extensions/DecimalExtensions.cs b/src/Web/Extensions/DecimalExtensions.cs index cbf1fd33..3794fefb 100644 --- a/src/Web/Extensions/DecimalExtensions.cs +++ b/src/Web/Extensions/DecimalExtensions.cs @@ -13,4 +13,12 @@ public static string Round(this decimal value, int places) var placeCharacters = new string('#', places); return value is null ? null : string.Format($"{{0:0.{placeCharacters}}}", value); } + + public static decimal? ToDecimal(this string? value) + { + if (value is null) return null; + var couldBeParsed = decimal.TryParse(value, out var parsed); + if (!couldBeParsed) return null; + return parsed; + } } \ No newline at end of file diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor b/src/Web/Pages/GamblingHelper/Components/GemStats.razor index 4ffe5a4e..d2817e97 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor @@ -1,5 +1,6 @@ @using PoEGamblingHelper.Application.Extensions @using PoEGamblingHelper.Domain.Entity.Gem +@using PoEGamblingHelper.Web.Extensions
Profit per Try

- Cost: @CurrencyValue(GemData.CostPerTry(ToDecimal(_values.RawValue), FilterTempleCost()))

+ Cost: @CurrencyValue(GemData.CostPerTry(_values.RawValue.ToDecimal(), FilterTempleCost()))

Worst Case: - @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.WorstCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) + @CurrencyValue(_values.WorstCaseValue is null ? GemData.Profit(ResultCase.Worst, _values.RawValue.ToDecimal(), FilterTempleCost()) : GemData.Profit(_values.WorstCaseValue.ToDecimal()!.Value, _values.RawValue.ToDecimal(), FilterTempleCost()))

Middle Case: - @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.MiddleCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) + @CurrencyValue(_values.MiddleCaseValue is null ? GemData.Profit(ResultCase.Middle, _values.RawValue.ToDecimal(), FilterTempleCost()) : GemData.Profit(_values.MiddleCaseValue.ToDecimal()!.Value, _values.RawValue.ToDecimal(), FilterTempleCost()))

Best Case: - @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, ToDecimal(_values.RawValue), FilterTempleCost()) : GemData.Profit(ToDecimal(_values.BestCaseValue)!.Value, ToDecimal(_values.RawValue), FilterTempleCost())) + @CurrencyValue(_values.BestCaseValue is null ? GemData.Profit(ResultCase.Best, _values.RawValue.ToDecimal(), FilterTempleCost()) : GemData.Profit(_values.BestCaseValue.ToDecimal()!.Value, _values.RawValue.ToDecimal(), FilterTempleCost()))

@@ -112,7 +121,7 @@
Average Profit per Try
diff --git a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs index 04ec1dd6..d048571a 100644 --- a/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs +++ b/src/Web/Pages/GamblingHelper/Components/GemStats.razor.cs @@ -75,7 +75,7 @@ private async Task UpdateRawValue(string? newValue) return; } - var value = ToDecimal(newValue); + var value = newValue.ToDecimal(); if (value is null) return; _values.RawValue = (value * CurrencyValue()).ToString(); await SaveValues(); @@ -90,7 +90,7 @@ private async Task UpdateWorstCaseValue(string? newValue) return; } - var value = ToDecimal(newValue); + var value = newValue.ToDecimal(); if (value is null) return; _values.WorstCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); @@ -105,7 +105,7 @@ private async Task UpdateMiddleCaseValue(string? newValue) return; } - var value = ToDecimal(newValue); + var value = newValue.ToDecimal(); if (value is null) return; _values.MiddleCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); @@ -120,7 +120,7 @@ private async Task UpdateBestCaseValue(string? newValue) return; } - var value = ToDecimal(newValue); + var value = newValue.ToDecimal(); if (value is null) return; _values.BestCaseValue = (value * CurrencyValue()).ToString(); await SaveValues(); @@ -146,18 +146,10 @@ protected override async Task OnInitializedAsync() private string GetCurrencyString(string? value) { - var parsed = ToDecimal(value); + var parsed = value.ToDecimal(); return parsed is null ? "" : CurrencyValue(parsed.Value); } - private static decimal? ToDecimal(string? value) - { - if (value is null) return null; - var couldBeParsed = decimal.TryParse(value, out var parsed); - if (!couldBeParsed) return null; - return parsed; - } - private class Values { public string? RawValue { get; set; } From be54a42880f12fd0b0a1dc1da6b99141e69ee8df Mon Sep 17 00:00:00 2001 From: TheSwerik Date: Fri, 4 Jul 2025 23:22:02 +0200 Subject: [PATCH 110/129] add MathInput in Filters --- .../Components/Filter/Filter.razor | 40 +++++++++++++++---- .../Components/Filter/Filter.razor.cs | 24 ++++++----- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/Web/Pages/GamblingHelper/Components/Filter/Filter.razor b/src/Web/Pages/GamblingHelper/Components/Filter/Filter.razor index 8e491ab6..41d60672 100644 --- a/src/Web/Pages/GamblingHelper/Components/Filter/Filter.razor +++ b/src/Web/Pages/GamblingHelper/Components/Filter/Filter.razor @@ -17,8 +17,13 @@
- -
@@ -26,8 +31,15 @@
- -
@@ -71,9 +83,20 @@
- - -
@@ -127,7 +150,8 @@
-