From 78c9c07c0efccd284bebe645e7c5f2db28b5731c Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Mon, 28 Apr 2025 20:20:04 +0100 Subject: [PATCH 1/6] Initial code creation --- .dockerignore | 25 ++ .../.idea/.gitignore | 13 + .../.idea/.name | 1 + .../.idea/indexLayout.xml | 8 + .../.idea/vcs.xml | 7 + AStar.Dev.Admin.Api.Client.Sdk.sln | 36 ++ AStar.Dev.Example.sln.sln | 57 ---- AStar.ico | Bin 0 -> 11562 bytes AStar.png | Bin 0 -> 12513 bytes CodeMaid.config | 82 ----- nuget.ci.config | 5 - nuget.config | 6 - .../AStar.Dev.Admin.Api.Client.Sdk.csproj | 60 ++++ .../AStar.Dev.Admin.Api.Client.Sdk.xml | 309 ++++++++++++++++++ src/AStar.Dev.Admin.Api.Client.Sdk/AStar.ico | Bin 0 -> 11562 bytes src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png | Bin 0 -> 12513 bytes .../AdminApi/AdminApiClient.cs | 137 ++++++++ .../AdminApi/AdminApiConfiguration.cs | 23 ++ .../Constants.cs | 12 + src/AStar.Dev.Admin.Api.Client.Sdk/LICENSE | 21 ++ .../Models/ModelToIgnore.cs | 23 ++ .../Models/ScrapeDirectories.cs | 37 +++ .../Models/SearchCategory.cs | 37 +++ .../Models/SearchConfiguration.cs | 92 ++++++ .../Models/SiteConfiguration.cs | 42 +++ .../Models/TagToIgnore.cs | 29 ++ src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md | 1 + .../ServiceCollectionExtensions.cs | 48 +++ .../AStar.Dev.Example.ClassLib.csproj | 9 - src/AStar.Dev.Example.ClassLib/Class1.cs | 6 - ...Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj | 46 +++ ...tar.Dev.Example.ClassLib.Tests.Unit.csproj | 25 -- .../UnitTest1.cs | 10 - 33 files changed, 1007 insertions(+), 200 deletions(-) create mode 100644 .dockerignore create mode 100644 .idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.gitignore create mode 100644 .idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.name create mode 100644 .idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/indexLayout.xml create mode 100644 .idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/vcs.xml create mode 100644 AStar.Dev.Admin.Api.Client.Sdk.sln delete mode 100644 AStar.Dev.Example.sln.sln create mode 100644 AStar.ico create mode 100644 AStar.png delete mode 100644 CodeMaid.config delete mode 100644 nuget.ci.config delete mode 100644 nuget.config create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AStar.ico create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/LICENSE create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md create mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs delete mode 100644 src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj delete mode 100644 src/AStar.Dev.Example.ClassLib/Class1.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj delete mode 100644 test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj delete mode 100644 test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd967fc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.gitignore b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.gitignore new file mode 100644 index 0000000..fbd23cc --- /dev/null +++ b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/projectSettingsUpdater.xml +/modules.xml +/.idea.AStar.Dev.Admin.Api.Client.Sdk.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.name b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.name new file mode 100644 index 0000000..c79273f --- /dev/null +++ b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/.name @@ -0,0 +1 @@ +AStar.Dev.Admin.Api.Client.Sdk \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/indexLayout.xml b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/vcs.xml b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/vcs.xml new file mode 100644 index 0000000..8306744 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Admin.Api.Client.Sdk/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/AStar.Dev.Admin.Api.Client.Sdk.sln b/AStar.Dev.Admin.Api.Client.Sdk.sln new file mode 100644 index 0000000..bb4c3b8 --- /dev/null +++ b/AStar.Dev.Admin.Api.Client.Sdk.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F881F2A5-6B1D-4E4F-A698-C3D5E760E509}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F205434D-6BE3-414B-A17D-A12F8E78C58F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Admin.Api.Client.Sdk", "src\AStar.Dev.Admin.Api.Client.Sdk\AStar.Dev.Admin.Api.Client.Sdk.csproj", "{1A33B6F9-9126-43D5-962B-6BA2DBE5B052}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit", "test\AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit\AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj", "{20915888-5AEA-4918-8DE4-FBE77EFCC758}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1A33B6F9-9126-43D5-962B-6BA2DBE5B052} = {F881F2A5-6B1D-4E4F-A698-C3D5E760E509} + {20915888-5AEA-4918-8DE4-FBE77EFCC758} = {F205434D-6BE3-414B-A17D-A12F8E78C58F} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1A33B6F9-9126-43D5-962B-6BA2DBE5B052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A33B6F9-9126-43D5-962B-6BA2DBE5B052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A33B6F9-9126-43D5-962B-6BA2DBE5B052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A33B6F9-9126-43D5-962B-6BA2DBE5B052}.Release|Any CPU.Build.0 = Release|Any CPU + {20915888-5AEA-4918-8DE4-FBE77EFCC758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20915888-5AEA-4918-8DE4-FBE77EFCC758}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20915888-5AEA-4918-8DE4-FBE77EFCC758}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20915888-5AEA-4918-8DE4-FBE77EFCC758}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AStar.Dev.Example.sln.sln b/AStar.Dev.Example.sln.sln deleted file mode 100644 index be68fef..0000000 --- a/AStar.Dev.Example.sln.sln +++ /dev/null @@ -1,57 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ADD5430F-CD80-42C7-80DA-90048E210EE7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{73794993-9898-4968-AF19-C3E7450C94E4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Example.ClassLib", "src\AStar.Dev.Example.ClassLib\AStar.Dev.Example.ClassLib.csproj", "{A9C19332-40FE-4E24-A890-405D46CD72A5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "end-to-end", "end-to-end", "{F1C7FB9E-2F0F-41C9-822A-7320339193CA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration", "integration", "{D724595D-C6BC-4F31-9D2A-4F4707436F10}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit", "unit", "{10DD984D-6788-4E04-A89C-3270006F5C56}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Example.ClassLib.Tests.Unit", "test\unit\AStar.Dev.Example.ClassLib.Tests.Unit\AStar.Dev.Example.ClassLib.Tests.Unit.csproj", "{1D7D41F6-3866-4C00-A1BA-1675227FA9FA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1CEEE40-22D0-4F7B-AB2B-A308F8DE6A54}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - build-and-test.ps1 = build-and-test.ps1 - CodeMaid.config = CodeMaid.config - LICENSE = LICENSE - nuget.ci.config = nuget.ci.config - nuget.config = nuget.config - README.md = README.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Release|Any CPU.Build.0 = Release|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {A9C19332-40FE-4E24-A890-405D46CD72A5} = {ADD5430F-CD80-42C7-80DA-90048E210EE7} - {F1C7FB9E-2F0F-41C9-822A-7320339193CA} = {73794993-9898-4968-AF19-C3E7450C94E4} - {D724595D-C6BC-4F31-9D2A-4F4707436F10} = {73794993-9898-4968-AF19-C3E7450C94E4} - {10DD984D-6788-4E04-A89C-3270006F5C56} = {73794993-9898-4968-AF19-C3E7450C94E4} - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA} = {10DD984D-6788-4E04-A89C-3270006F5C56} - EndGlobalSection -EndGlobal diff --git a/AStar.ico b/AStar.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b9aea493d206b4ce2c3a43915458e568ca18ec4 GIT binary patch literal 11562 zcmch7WmHsO)c4Sx5=w(~#}G;jh`;~}NOzC4NXG!uASgM6l(ckrH$w~3&5#m;G&Aq; ze?GpSpJ!&xn!D~@cb|QJJI*cu01fqh{Gb7t02F)x02S&w>aB({5k3t*>XJz1jiT1$ z)5jk!HtNs#W3ep&z`LZPD6iwa_~*M9)WqXE(rnFroNq=XEkT9dM%6bP$b=h=`PTLQ zya!)XOv^#Vp^Jx2uu^&r{ybIK`zrJPXcm0V_S8uwU$s}bcwCA5?xW3Dndc7=XYJy3 zY1YD5^PeR?Hm7v4{wnp)Fns)|e))=G=)JBv|3In@hZ5 zKX89V!OFKs_|BwYzDl(QjRnxtR8Xi|?V0989B4P6z{0qu zxZm^lx?88@OIZU~yscq+PBc^L$OKbI@jYgK|xXd&kD3!~< zItHPZJTB&*pmR2p_uC$$XY3GO01(7U_p!6mJaQ^P1vKEbZ(wpx+d~@d3+r}|+=z)3 z4Ou-ti=eUMj`XI4mf8(*8vl=>GX@3-}w=I$$1@3 z!f9tk-VQpg)$f}|hC+{mQQLtc zg0F!&c^@J96dg1m$1_*%TKqMtn+5R74aMEq(X+Y3ADzUPgkE9Hw$=U>{rBuXypJ4j zUe^ zDnG}T)2J?%dPR`6pTd~HwfCHBCL*uISh;9`wUFHpH(rXhWBS3M$_z}1cwN{63( zq)?28z=y;lMpWx3ARMXlCZ4b5G$RLmV-&1)peSgYl9ZD7A4mlM-M=J}1xgCdhd-Hn zsh>cI!WBP}t(K4K*y#S7azuMCFYycOZ|b%G39Dy2QY=Fyc|_-I=fk6Zz(9mqx|RC1 zZCr`wA_ zk*+$W=t`*lS;CV*aWdZ{4m!Ihp>QTmw_o@fi`@S$7P3AhmI40v?7UMT$?RMk-w4`w2HQYU)?=DH;Z2vn1Mc3 z$dT|*8{1a>kE)Q(EP6)aYX`HWtZy=jE6kA#yw3Idlf3aUC^If>yK^1nMeN~!mh1`i z@zl{*hSvNAtpz`tDv`(mAF)ZM5K^+I5YIk;s>^f>o36emw+8fxoCQc0s1einYa6 z+%8n@K8+!H7aoGUT8Z5w!wJst@mK&`&}0HfLmEm}z9#@Wrxpd$Mg!p`+rUJ9)K+_n zKU!y~gPTY4cQU7hkhRt;K@1VMNBF|>C-R`3N?Ob(bwI{!NlZfq#rG{lnQT@G5iMXFMfBEn=d9&($x+yv*rfnS-x83Pwq8( z@4y+w2^TkzXt+W4<^u5{S8=4tHgG#kn!;H_UdL)-_v;w@OHbveJMRy9KMPnR$#!m&Z3Ucy6?F-m5L6sJI6bM5i`EQuByhNj|xk45l z^R%)JIA=`^mmXSjym%zCTU&(@CuD5e>4(Q>dn1uEuG!ffrhmsKhBo?cWO}Lk@+gA( z%c-dSU#hAn(=?Df5P8f-H;cHR8}brpTci@uh~#om`@@V-$Jmc;o_RxEi4}-KJHsp( z`poa#Zca{L!GpW;BoC=A{%UPDA}FJrnjVq07H$!#E~@8?bA3{8v`?b?K;iAKC?^Vxhal}#U4l>+PMR1 z$X_$9F26kYD`Jl&U9372%3fPe;D;IkxrKsyQy2ZWYTwQA*N!Pvi{MY_baZSXg2dT1 z8Q*_+&o1WUvlwy8djR{a3-3-~C|2LNNjGA=l=zdtI^o0@OEFQ52ax;2+TY9=e>&Ak zAXK6yZ-;AI47}UjF^^lOXZWVTbYuny0;@o0aFQ9gs5|cND9rAI)p!jg#LacEm_`TJ zo^1ZijuI}=kk2N{;9jpU#tVuf-Ts2m2|5J(KirOa@^a!CC@QFin&%S-zUhsXsJm1E znvecOvx7|REVjN((a^xzG)dy7)x{cJRu1Ypa6{X`^1ofIFi{U^%kUKV=nZ{2*B!^>`@M}vQ zK$uLUsFS}4!U{t%I)I1a*K_J`PPb*vg|QQbYg?Ow^O8YCgkL4 z^xi%4Tes;1C;Qta(s!Cd9&8xtKNZTpzhj~1qXrg29c<}RJ}DGToM&Jl9%xlMAnNH^ zh#thX6*s_~J1@(dYrR|~irdXx>Km(f0w2x(clL};fDc+5tN%`jc3F%XJ zg7q&KeZb#qhf|CZY~^A*9%vxdU>Eu9J}hEIsq&lKhwoCI;-cG-H7)wh$h3lwXTmD0^>IYbXtf{eCQ4S`6DLMQu>(poOS+*CiM=^L(a6z?HFZQt5g(pwP+R=N7imSiPYUqmMvg?oj=0{XPj zs8^PT;T4U)p_R_D!sW})1jW8y07Xja;+)CF&EHb6S9X@smP&j6nezX)%f)}M5`B5R z8AwZ62LSXXx*jSp2sqEcoa&%2F^Wg?n_5I(97Q>|b9-Ahj`q_^Mis3g&iG%9 z;yFXQp1Y!3h4bwT(d%xKyWXWp{5fls%uQ5toD)y2$t_4qPY5nAEV^pCcS_}XbvxH< zwx8A*s7)x2d)?~xpMwv*QTB_%x50KOmP{uv~WX~_bc=E4I&u;ZMs@Xq+yaWZ=4es?Zx17 z2b+c;EPGxzXyD>@;#i!m-G*Vp8UMO#0k7kXfVxr8@S39mV12W-!Y~Mt8FwP_eM{Aq z$M77JaPyP)Bdx%hf>I~T)JnstvS4meh_x$5`z zy~UV|vxa+5i5tQ{Tb%#?!Ho?7m~h0q?h8}tc!)^?*Jh>($}DZ8A2!o*QG6N*aEAD- zudi<23dR&!55zd=>r>nM^maMRLAcl9V?v(<(E_-zKyDWAIbd%lmx^vSuZ(59+!FLW z<6Jt6_j)^)Jh3uwR#=NNv(0CC(0=8sV^N9n;2st%I`u%-!)7^bz9-Z>k{PE2-o2;MynmmvA80_mbbHvtaI~fec*EdD^Sy zhn?Ub$e4rYycHy!KE$2?f)BmCE|J97YphaZ>KRt{kj%~-VByv$192dGR4ZDl+4s$D z0e0`r0+zB}N;L29C^OSEhHjs)sn!JO0TO4b>Xz~e2S44bbYRJy&5yHA96gg*t?npE zIv~GuT5FWDbOVj9zlV4^eW`7C$!tMec%8NIP?+zf8huGTHrSc4eW8}vM_{j;jVnjo zmzaL?z})Yrsd;)`E2Cqb*INFtHOPhw6nBXxQ|ny0uf#QSwCAF~Agy#^XydfkwoAup zVq`E3%rg{QVZ{JO}FOjz`N@^?v)Os&Kyv1T;Jd)_+O~BzBy{4n1uQY%0rXHrKfBWy_r)rOD zx~^GuaM2mA5}o^wVQ5)T$gev%FVN9eofSzrR_8V!1+m{xjTQVNN+9xXmK+<Vd;g2WD&RC!RskPNmrHpAVyL=He$hFJSgwU9<#xu06DjO=rp*%V| zZ*q6A?CQ)iqb)3Fu0iraaX9f+@6t2G`rlChr2T55+tNmB%NkjP9vB#q9uFyK8t{k$ z!~z65sZP7yH~s(YrY#=t)PaK?A>7MTs*&3J2%9pbvm**BPbLxnFY4cJ~i29?Ts&ygZ_>zBBB`DNFl<-Ko%?#;Px2Aht48_?vnP zWGmDarb_umJc=h*1D~Bk)ULV;N|i;Ewe)OB4BAGXr}+lUd!No?8oeY~CaS74=xJ_w zQrL2|Kbh&Nk{}}B?&DMmy=MU^n0pn$+ZdZNZ%k74JgrM{^gQL=%W@b3CkQ~U&Our$ z(_j1D$`!HJ~i~HYdWji zg;Oi~u7PcSPQ(@cZpeV`7h~v6_KuvAJc%7a9R4t_{hM?|TnT@G+t}}lPd9r=*7-V< z%mA_=sY&YJ_q&Y={s(!i-&be~TiA=1$!Tk=t(+I3$W;J_r0T1Y{EIS+Of8$}5bROx zql5FN^Br`3uSCA1VMCj=(hicW#C0B7&jOTcGfm*|V|BI(jYW*BxFW4&l5W@)1dCiQ zYBBK}J{9Ix8w`hNC^%YVTX5VMv*V{e9+QrAKM=G&urcy?|vglcFo39WwmK4 zks=$nYsl@em*FvZLYk%t@8>U62l&9>Jwd~|>L9oCJLy9FY55$B%Kk7Yv7{SBhTZ>I z?P2Q_sq-OWnAM^1vQcB1%NE|3~nC2Ao9USY+Cu}bcYgi>cvEHT%m-Ou8C5F>j zByf!pWmpmdD=Jy@B^nASZ0tPQi4Wr~9|Cq9^_PHf2tv?Z)B6}^73&l#~-)>Oi>tV6t#OGXyl+Ay%NAnuyvpw~Ge zYf|ezbk_L*?yKnNeh!8#?fku9NUg6`eeD?>IY41Cb{w%t(H@Y_jIk*%Cfpg(OnCHMvQ zm5ivG)Z51uk3y<_X(s~}s!__RcTn4Fq?^eDlk7w|Xm_$#NY3waf$JU00f?OtOmUfY zBA#YuLLxkLI19SW@_o*rpN87$mctC3NUU)!_BqMUZb|#Yj)b3O*v6Gi{osyE>uYmI zZxvf9ufo!k5r-N7%~$&?)M%XKrYrQPW~r<rmdCB|BOVRIb_jlp?-To8D)~J|k z^XtSP`Go-g9b3$W_)7J&VS*=ZP3n$_DLG4kPo6-Q76X(@kOY4^ysCuVd$5C0BqHsSBK}16{#L5lqdZrcBqSUbaqk^`U1EEC40YIWQ)j+h+A%dLt8Js>!lG$M-n{$uTal3w>d8 zQ~A2C>z}Be^o%kRu+#Za5@aMcc(o)4R*H#*`jdPw^?#Jr3d3=kL4}f76-UoK*9TcetKnup#9g z1H-i!>-6Jsc;2cA1iWNf@3lZxzG5hu(BJZ+a(`{v7gmTXxIS?i3bl_oCbtIKmhPXl z_lE+R%ME1Mdh%MjSAtp_2Ykw9Czd|9fG+dFrI!8vK0qZvbSNs*XU}XAC)}qK0Gl@; zE{LWfoo&oL_lu`P4J;1DT(t&kQr+PXP7^<9a|EjsD+Ir3^)tsf`cx?b@Uw3} z^oQ6I_%m&6J0cj1|9L8?J=^&)^{wcUb>hW`)C*W(2-BK#*Q(%km4Agg_>C!XX|kgs`8M}RI+B&=m25ap$0c0VFG@?SKViGukJWIIq0$)a2aYT2h@|cF^NX-9 zy)vlXjFdX29A-|}3(qg=;INQm!9I62XsS42RxYni%TMpb~C9NBuw6Eb|tLBO@Cd;%c%u;>h3P$y(3o`;Bb5#DJ+< zqC+Pj9NVbk)Csmc@F*q*U!tK0D+LPfV%`iZA(uj|3Y!C@yL@-AWJ237p(<30Fs6u9 zc2`jzAkx>8MaA1|4Vaamc7S{+6wbtW16mukVC0d9t$AMdxKZM zk!99PmU9vr&HQ(g`LpM z7;*_kF_rL%6&=02IytZ5m#w@BbRfF=OXL+RM%MCHP3=T4L7l-bE3U#6N#5_|&wy_w z9MOtZdvm{E6!^zI#5WxQo-(se2!rCx4$UKizgxJTpJSKtG`?`wxB=k2if;z#CF|2o z>GUjN(5FqLOiZMD{PcaL^9zU0EAlzI;HyXiK|<9Z(FQ~*DoBs;^VHPegv)tGxMJx8 z{u7M>GOyw11+yKnk=yrTlm(AlJ~kWZui|r{j== zs{NvY(?3*y>~Nxj97PRbJyQWzap0q&dSAKD37kQwL#@{K+gRN#=%1O{8yNCip(LhT zhEFX;jA0+DL-N#$O3Vme%2vJ{0l~fFkYvs0P?X4r)l;LIpwXotQN5M4>8Q)Sy}BKZ zPyRM#031|*k(o7_hXr8OU-N=0#*pug{v>4rfw194c-MQnmifYhcMJw(K|-6T#zR5D z$TdPp`5rk%5d>8?skdCg7eYf34jQR}h0=tj8SPRo?Ei-T>DLX`p(3X2?+D@HAyh7} zdTh}>4Je%DN`Z~T5KBg(`BCPophL1@82>3=pS3k|JlZQfMn%^I3hG8Sa zdlXh6{T6=yD+W!#i701Te6DL*CK5YL03CnE?V0!!sI`7EZdb9EtbyCF=*BVx|Mus~ zpm5f*qtHT$D4ni}3QhN(-K|XT*O{^_8yh@C?zdk^e-+{**{yLZ(NCm)efy5)(}O*p zI$8xmfwz&!5ng42(~y+$?S0twJs3vYUe5@>l5h6e%D(zcVTSmo=G9r)Zsh*Z&hUVT z>Xm2~3aE`DuhTW64)#fQq(OUgox82-*r@z*+jRN@U{7W~=GLMOE35Jhc&t7%32 z_b>msZKJ5(S_1s@E^E`KCRdv{Y$lRJ)U0!Q=eZfkb?Nm)3Jz=%(M-DxOOd5$!p2iGDz3b2jghDO|#N0lLhc=5_$D8#8C z%m&RqXZmx0yC4rS^VmxKt^ch9UPa4nuR7zk@&(8G@nArfw~+a}TBTlK4m*}jR#Y4H zavKH~kxC}%{r)j9K*;B{yF70YhU4jLRe#}DkU}6Li zrw@IeB#y`~i`;gDj)76c4lxb#tz^TV3Mw+gzizB#zd7>&`?`BS_q+n5s+LwW zKhd1r9ZQSspXlM@4A7hoNXQif)j4D;+0N`2HGi#g8od+l74;>HPF*E{NPJ5#SBWg4P#A^lmf@#kvxu4RHX zLW1wcT>EllL1dP%+w*EbY2s$cr@m@5k++4eMdhfd+0mD6y><=sx6(}g^x*jZicPjt zbiH?%P2%cK#dN7WSB!|KHFlPaW@|GS#@Vo_5*BzMF zL(-gwo6Hl8vA26IVK8JPQ${PD85SB5=B&m|sG9a}BX*ret$k(`9%%}utyqt}uJQ+v zk^5&gFa@eyrmP&#+2u-l2PTcvhYOad%KIJv>?Fa*g2gI~9`3iR1txCR{3MZS7biFN ziyzd^z|cUYQAHYrPtqyH_j|^6szhG;{w|gzd_c{T0Dn@?qas&$EfG#N0GnyMi&~Z} z*CrgxNKGAyd_&L$*l*`BJ-vbbPBeCNO7HOAFwU-!8KaN)ZL8ul?_UPBR1Ji^G5^5! z`nky-Px0wnlwI9zEVi=IOVF|6N&tvNslIrm7(CE3Vm(*E&`w~V6?-0h2oCgCkA5=o z<}~$AIt1OnnN9ojFk_}|w@%{sa>_t$9a+VDlOKuvzo1RAMfPd7@7R9!hKK0`FkCNG zGawMiQyvT28PEI?i&~HS+D*Yq#!~;6FGaI&UMILbyzF0PIdxcS1&TXgfKTR7M!{7{ zl*2FLXr13mfPwV|uau*kh_*n|RRT4P1K^;`GI&^#4(XKz+Jb&hGgw`w-Yj21Cx&;~ zQ2Gl_P@2rkE)b|_zG&oH_4Cd2WB7;n3JrEBQZib4F?6qml@EGZRiov0M1yCC@)E&d zkLcdimjm}Fxbd(f2o~&3S2RB~izzJx*)H6VqS>^QyZO(CF>qivplI->Ta@cQ2sCS% z>gMt~q_K=tfQJk8Th)c?i$_W5R}yU#~O?%UTH z$AM$d*4%D>$B-Y*m*@70>yEc|d@EsErVXyMbbB1K_$v_u*d9V1*@@gDE-8vxFr*_r z#036Df=7dU^8R)Jcp@V@&Uo_o&6gicp1ip?!~ejD<6jcuD<>OEXs~1Pg1%XwPk1#7 zuYBnb94JS3hqidpzhd0!LX4ojC%YRL4j^Ag4aTy)@B?7wG2!)5zlw~HMS}X$pbO&G zMc~GNZyD3J~@DSK`~tWOj(bBfWEm7LVmRrnYnME3QVrLAvg?IW7pMT?Wl?+ro4MQCQYK# z{YM)UfJ)YV(lkJBkJkkCK8*+E*I~9@Q3&nKT&EVcYjH0(^k)K zS^b7%!lq+}czgZooMD!PKOAq23X>#K1JTRPqt*UdqqO_hae?`LFbe?3kJ(Y;@CTX5 z{Z>8a5%)91MkOetq^(>C*Wow-w)@M4;}JNmeK(H<;%IMjXWuTXQHS&wU#N&?gxtQl z0;(rZZ}BJw-v89F(!AW?OJHWk`OK;*l9bA6E2a;(7$HVVKqKB+(UtDwX%Cw1ki~-^ z8&WZlKY!CHN{(7VdMoMhGiv`1SJhC640GrVx%fZyjL`^jk6}G_n0@df&5nZRhqpQxNAlLJJUC6v_P-mBh8Gc>HeOO%fX*k z(NS9?9Z`GjS9EiQ+3V&!xEKV5>}fh!kQSpBjEHDVG!eQ~lZWqBB!TBA6uW_Tze8j9!(5#Cz+p8Ct3B$78Cwy#%@ z0{tb|3-3_X!_sFnlxic1UR9s|rSlF)+4cGC;W5$=H|dPFs<)iVwV_=8#;9ZA?C=k1 ziavAzV*i@-#DF?voYav|1YFR~Q1ev`Lz3#Mi*>?rqgl8gWoru!YDW*olU)ra@IUk) zDG1W}^6EQ=De}X=@?TK?YSqzoVDK5(-RJu#OWYUgm68;a0#2z8vy1}^zWXfoJvpmn z&*4qTqYl5g`{Ehk_9akvdomk$5NQbhV2cW||{qh}HX)*)*}xJ zT>S5oUC%#f{W6OL5XPa)oFfDsmgt_yE$UvvA(LN$9*1A}gGsLLXLp}avo;+HL0*|w zRd5P$zi&Rx-D$RO#RT~NvbXjHM-i{rFvNQ zKVRRJ)U&_;ixQ5jZRSsB(3XR_p=asG&2GV8`SdQS)~4B*^H$CH^pX`@QA>SqVe zKKjvL^t>FLedd2OGNL9$qmlrNbv|oXp%ZX7DPKNnc1OY-pY$+#Ec&iLBtC5?7ZRvG zQ=R~SM5jLZWZL*bJM{{}+-;UEy5CuY-+$rDg-a8QXM}&>r-oMPNN{~)HvJgfUV4sp zGm>D^I0wvcHJ7925O`ri4s-@|EsQAoT@4l^)=GYcONeQ^1(zizkllD_ysPS0R p)z~@^-v7y5@BcqN+#`5{v=my7l1ov{M@>5eRFpInD_)t0{6FsrHK70i literal 0 HcmV?d00001 diff --git a/AStar.png b/AStar.png new file mode 100644 index 0000000000000000000000000000000000000000..8cac0a1d8136c82d5b582bdea30f24bf2e085e0f GIT binary patch literal 12513 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 MOs&qBpL2=*KT7L~lmGw# literal 0 HcmV?d00001 diff --git a/CodeMaid.config b/CodeMaid.config deleted file mode 100644 index 54f2ebe..0000000 --- a/CodeMaid.config +++ /dev/null @@ -1,82 +0,0 @@ - - - - -
- - - - - - <?xml version="1.0" encoding="utf-16"?> - <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <string>ReSharper disable </string> - <string>ReSharper enable </string> - </ArrayOfString> - - - - True - - - 1 - - - False - - - True - - - True - - - 1 - - - 2 - - - True - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - True - - - 1 - - - - \ No newline at end of file diff --git a/nuget.ci.config b/nuget.ci.config deleted file mode 100644 index aa5beec..0000000 --- a/nuget.ci.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/nuget.config b/nuget.config deleted file mode 100644 index 782724b..0000000 --- a/nuget.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj new file mode 100644 index 0000000..fa4f26b --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj @@ -0,0 +1,60 @@ + + + + latest-recommended + AStar Developement, Jason Barden + AStar Development + AStar Developement, 2025 + Update. + $(AssemblyName).xml + True + True + true + enable + True + true + enable + AStar.png + LICENSE + https://github.com/astar-development/ + Readme.md + Update. + True + git + https://github.com/astar-development/ + snupkg + net9.0 + AStar.Dev.Update + 3a340cb0-60ee-4e38-93ac-0f8829c7193d + 0.1.0 + + + + + + + + + + + + + + + + + + + + + + True + 1701;1702; + + + + True + 1701;1702; + + + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml new file mode 100644 index 0000000..cb8ef56 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml @@ -0,0 +1,309 @@ + + + + AStar.Dev.Admin.Api.Client.Sdk + + + + + The class. + + + + + The class. + + + + + + + + The GetSiteConfigurationAsync method will get the User Configuration. + + The Site Configuration - populated or empty + + + + The class containing the current configuration settings. + + + + + Gets the Section Location for the API configuration from within the appSettings.Json file. + + + + + + + + + + + The + + class containing the project-specific constants. + + + + + Gets the Api Name. + + + + + The class. + + + + + Gets or sets the value of the Model to ignore completely. I know, shocking... + + + + + Returns this object in JSON format + + + This object serialized as a JSON object. + + + + + The class containing the Scrape Directories Configuration. + + + + + Gets or sets The ID of the configuration. + + + + + Gets or sets the Root Directory for everything - search and save. + + + + + Gets or sets the Base Save Directory for the saving post search - appended to the root directory. + + + + + Gets or sets the Base Directory for the search checks - appended to the root directory. + + + + + Gets or sets the Base Directory Famous for the search checks for famous people - appended to the root directory. + + + + + Gets or sets the default subdirectory name for the save - appended to the root directory. + + + + + The class containing the full Search Category definition + + + + + Gets or sets The ID of the search category. + + + + + Gets or sets the Order of the search category - i.e. which category should be 1st, 2nd, etc. + + + + + Gets or sets the Name of the category. + + + + + Gets or sets the Last Known Image Count. + + + + + Gets or sets the Last Page Visited number. + + + + + Gets or sets the Total Pages for the results. + + + + + The class containing the full Search Configuration. + + + + + Gets or sets The ID of the configuration. + + + + + Gets or sets the Base Url for the login and search. + + + + + Gets or sets the Login Url. + + + + + Gets or sets the Search Categories. + + + + + Gets or sets the default Search String. + + + + + Gets or sets the TopWallpapers something. + + + + + Gets or sets the Search String Prefix. + + + + + Gets or sets the Search StringSuffix. + + + + + Gets or sets the Subscriptions something. + + + + + Gets or sets the base Image Pause In Seconds. + + + + + Gets or sets the Starting Page Number. + + + + + Gets or sets the Total Pages for the New Subscription search. + + + + + Gets or sets the Use Headless to determine whether to run in headless mode or not. + + + + + Gets or sets the Subscriptions Starting Page Number. + + + + + Gets or sets the Subscriptions Total Pages. + + + + + Gets or sets the Top Wallpapers Total Pages. + + + + + Gets or sets the Top Wallpapers Starting Page Number. + + + + + The class containing Site Configuration. + + + + + Gets or sets The ID of the configuration + + + + + Gets or sets the Login Email Address + + + + + Gets or sets the Username + + + + + Gets or sets the Password + + + + + Gets or sets the Site Configuration Slug + + + + + Gets or sets the Base URL + + + + + Gets or sets the Login URL + + + + + The class. + + + + + Gets or sets the value of the tag to ignore. I know, shocking... + + + + + Gets or sets the Ignore Image property. When set to true, the image is ignored irrespective of any other + setting. + + + + + Returns this object in JSON format + + + This object serialized as a JSON object. + + + + + + + + + + + + + + + + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.ico b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b9aea493d206b4ce2c3a43915458e568ca18ec4 GIT binary patch literal 11562 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 literal 0 HcmV?d00001 diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png new file mode 100644 index 0000000000000000000000000000000000000000..8cac0a1d8136c82d5b582bdea30f24bf2e085e0f GIT binary patch literal 12513 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 MOs&qBpL2=*KT7L~lmGw# literal 0 HcmV?d00001 diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs new file mode 100644 index 0000000..1dd1303 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs @@ -0,0 +1,137 @@ +using System.Net.Http.Json; +using System.Text.Json; +using AStar.Dev.Admin.Api.Client.Sdk.Models; +using AStar.Dev.Api.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Identity.Web; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +/// +/// The class. +/// +public sealed class AdminApiClient(HttpClient httpClient, ITokenAcquisition tokenAcquisitionService, ILogger logger) : IApiClient +{ + private static readonly JsonSerializerOptions JsonSerializerOptions = new(JsonSerializerDefaults.Web); + + /// + public async Task GetHealthAsync(CancellationToken cancellationToken = default) + { + logger.LogInformation("Checking the {ApiName} Health Status.", Constants.ApiName); + + try + { + logger.LogInformation("Checking the {ApiName} Health Status.", Constants.ApiName); + + HttpResponseMessage response = await httpClient.GetAsync("/health/ready", cancellationToken); + + return response.IsSuccessStatusCode + ? (await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(cancellationToken), JsonSerializerOptions, cancellationToken))! + : ReturnLoggedFailure(response); + } + catch (HttpRequestException ex) + { + logger.LogError(500, ex, "Error: {ErrorMessage}", ex.Message); + + return new() { Status = $"Could not get a response from the {Constants.ApiName}.", }; + } + } + + /// + /// The GetSiteConfigurationAsync method will get the User Configuration. + /// + /// The Site Configuration - populated or empty + public async Task> GetSiteConfigurationAsync() + { + string token = await tokenAcquisitionService.GetAccessTokenForUserAsync(["api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write",]); + + // logger.LogDebug("Token: {Token}", token); + httpClient.DefaultRequestHeaders.Authorization = new("Bearer", token); + HttpResponseMessage response = await httpClient.GetAsync("site-configurations?version=1.0"); + + return (await response.Content.ReadFromJsonAsync>())!; + } + + private HealthStatusResponse ReturnLoggedFailure(HttpResponseMessage response) + { + logger.LogInformation("The {ApiName} Health failed - {FailureReason}.", Constants.ApiName, response.ReasonPhrase); + + return new() { Status = $"Health Check failed - {response.ReasonPhrase}.", }; + } + + // + // /// + // /// The GetModelsToIgnoreAsync method will get the models to ignore. + // /// + // /// A collection of 0 or more models to ignore + // public async Task> GetModelsToIgnoreAsync() + // { + // logger.LogInformation("Getting the models-to-ignore."); + // var response = await httpClient.GetAsync("models-to-ignore?version=1"); + // + // return response.IsSuccessStatusCode + // ? (await response.Content.ReadAsStringAsync()).FromJson>(Utilities.Constants + // .WebDeserialisationSettings) + // : []; + // } + // + // /// + // /// The GetScrapeDirectoriesAsync method will get the Scrape Directories. + // /// + // /// The Scrape Directories - populated or empty + // public async Task GetScrapeDirectoriesAsync() + // { + // logger.LogInformation("Getting the scrape-directories."); + // var response = await httpClient.GetAsync("scrape-directories?version=1"); + // + // return response.IsSuccessStatusCode + // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants + // .WebDeserialisationSettings) + // : new ScrapeDirectories(); + // } + // + // /// + // /// The GetSearchConfigurationAsync method will get the Search Configuration. + // /// + // /// The Search Configuration - populated or empty + // public async Task GetSearchConfigurationAsync() + // { + // logger.LogInformation("Getting the search-configuration."); + // var response = await httpClient.GetAsync("search-configuration?version=1"); + // + // return response.IsSuccessStatusCode + // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants + // .WebDeserialisationSettings) + // : new SearchConfiguration(); + // } + // + // /// + // /// The GetTagsToIgnoreAsync method will get the Tags to Ignore. + // /// + // /// A collection of 0 or more Tags to Ignore + // public async Task> GetTagsToIgnoreAsync() + // { + // logger.LogInformation("Getting the Tags-to-ignore."); + // var response = await httpClient.GetAsync("tags-to-ignore?version=1"); + // + // return response.IsSuccessStatusCode + // ? (await response.Content.ReadAsStringAsync()).FromJson>(Utilities.Constants + // .WebDeserialisationSettings) + // : []; + // } + // + // /// + // /// The GetUserConfigurationAsync method will get the User Configuration. + // /// + // /// The User Configuration - populated or empty + // public async Task GetUserConfigurationAsync() + // { + // logger.LogInformation("Getting the User Configuration."); + // var response = await httpClient.GetAsync("user-configuration?version=1"); + // + // return response.IsSuccessStatusCode + // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants + // .WebDeserialisationSettings) + // : new UserConfiguration(); + // } +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs new file mode 100644 index 0000000..f836811 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; +using AStar.Dev.Api.Client.Sdk.Shared; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +/// +/// The class containing the current configuration settings. +/// +public sealed class AdminApiConfiguration : IApiConfiguration +{ + /// + /// Gets the Section Location for the API configuration from within the appSettings.Json file. + /// + public const string SectionLocation = "apiConfiguration:adminApiConfiguration"; + + /// + [Required] + public Uri BaseUrl { get; set; } = new("https://not.set.com"); + + /// + [Required] + public required string[] Scopes { get; set; } +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs new file mode 100644 index 0000000..bb683cb --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs @@ -0,0 +1,12 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk; + +/// +/// The class containing the project-specific constants. +/// +public static class Constants +{ + /// + /// Gets the Api Name. + /// + public const string ApiName = "AStar.Dev.Admin.Api"; +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/LICENSE b/src/AStar.Dev.Admin.Api.Client.Sdk/LICENSE new file mode 100644 index 0000000..0b1b024 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 AStar Development, Jason Barden + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs new file mode 100644 index 0000000..2e6317c --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs @@ -0,0 +1,23 @@ +using AStar.Dev.Utilities; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class. +/// +public sealed class ModelToIgnore +{ + /// + /// Gets or sets the value of the Model to ignore completely. I know, shocking... + /// + public string Value { get; set; } = string.Empty; + + /// + /// Returns this object in JSON format + /// + /// + /// This object serialized as a JSON object. + /// + public override string ToString() => + this.ToJson(); +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs new file mode 100644 index 0000000..5d2522f --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs @@ -0,0 +1,37 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class containing the Scrape Directories Configuration. +/// +public sealed class ScrapeDirectories +{ + /// + /// Gets or sets The ID of the configuration. + /// + public int Id { get; set; } + + /// + /// Gets or sets the Root Directory for everything - search and save. + /// + public string RootDirectory { get; set; } = string.Empty; + + /// + /// Gets or sets the Base Save Directory for the saving post search - appended to the root directory. + /// + public string BaseSaveDirectory { get; set; } = string.Empty; + + /// + /// Gets or sets the Base Directory for the search checks - appended to the root directory. + /// + public string BaseDirectory { get; set; } = string.Empty; + + /// + /// Gets or sets the Base Directory Famous for the search checks for famous people - appended to the root directory. + /// + public string BaseDirectoryFamous { get; set; } = string.Empty; + + /// + /// Gets or sets the default subdirectory name for the save - appended to the root directory. + /// + public string SubDirectoryName { get; set; } = string.Empty; +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs new file mode 100644 index 0000000..09f062d --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs @@ -0,0 +1,37 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class containing the full Search Category definition +/// +public sealed class SearchCategory +{ + /// + /// Gets or sets The ID of the search category. + /// + public string Id { get; set; } = string.Empty; + + /// + /// Gets or sets the Order of the search category - i.e. which category should be 1st, 2nd, etc. + /// + public int Order { get; set; } + + /// + /// Gets or sets the Name of the category. + /// + public string Name { get; set; } = string.Empty; + + /// + /// Gets or sets the Last Known Image Count. + /// + public int LastKnownImageCount { get; set; } + + /// + /// Gets or sets the Last Page Visited number. + /// + public int LastPageVisited { get; set; } + + /// + /// Gets or sets the Total Pages for the results. + /// + public int TotalPages { get; set; } +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs new file mode 100644 index 0000000..7f3bea6 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs @@ -0,0 +1,92 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class containing the full Search Configuration. +/// +public sealed class SearchConfiguration +{ + /// + /// Gets or sets The ID of the configuration. + /// + public int Id { get; set; } + + /// + /// Gets or sets the Base Url for the login and search. + /// + public string BaseUrl { get; set; } = string.Empty; + + /// + /// Gets or sets the Login Url. + /// + public string LoginUrl { get; set; } = string.Empty; + + /// + /// Gets or sets the Search Categories. + /// + public IList SearchCategories { get; set; } = []; + + /// + /// Gets or sets the default Search String. + /// + public string SearchString { get; set; } = string.Empty; + + /// + /// Gets or sets the TopWallpapers something. + /// + public string TopWallpapers { get; set; } = string.Empty; + + /// + /// Gets or sets the Search String Prefix. + /// + public string SearchStringPrefix { get; set; } = string.Empty; + + /// + /// Gets or sets the Search StringSuffix. + /// + public string SearchStringSuffix { get; set; } = string.Empty; + + /// + /// Gets or sets the Subscriptions something. + /// + public string Subscriptions { get; set; } = string.Empty; + + /// + /// Gets or sets the base Image Pause In Seconds. + /// + public int ImagePauseInSeconds { get; set; } + + /// + /// Gets or sets the Starting Page Number. + /// + public int StartingPageNumber { get; set; } + + /// + /// Gets or sets the Total Pages for the New Subscription search. + /// + public int TotalPages { get; set; } + + /// + /// Gets or sets the Use Headless to determine whether to run in headless mode or not. + /// + public bool UseHeadless { get; set; } + + /// + /// Gets or sets the Subscriptions Starting Page Number. + /// + public int SubscriptionsStartingPageNumber { get; set; } + + /// + /// Gets or sets the Subscriptions Total Pages. + /// + public int SubscriptionsTotalPages { get; set; } + + /// + /// Gets or sets the Top Wallpapers Total Pages. + /// + public int TopWallpapersTotalPages { get; set; } + + /// + /// Gets or sets the Top Wallpapers Starting Page Number. + /// + public int TopWallpapersStartingPageNumber { get; set; } +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs new file mode 100644 index 0000000..e7a7666 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs @@ -0,0 +1,42 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class containing Site Configuration. +/// +public sealed class SiteConfiguration +{ + /// + /// Gets or sets The ID of the configuration + /// + public int Id { get; set; } + + /// + /// Gets or sets the Login Email Address + /// + public string LoginEmailAddress { get; set; } = string.Empty; + + /// + /// Gets or sets the Username + /// + public string Username { get; set; } = string.Empty; + + /// + /// Gets or sets the Password + /// + public string Password { get; set; } = string.Empty; + + /// + /// Gets or sets the Site Configuration Slug + /// + public string SiteConfigurationSlug { get; set; } = string.Empty; + + /// + /// Gets or sets the Base URL + /// + public string BaseUrl { get; set; } = string.Empty; + + /// + /// Gets or sets the Login URL + /// + public string LoginUrl { get; set; } = string.Empty; +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs new file mode 100644 index 0000000..94a3dfe --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs @@ -0,0 +1,29 @@ +using AStar.Dev.Utilities; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +/// +/// The class. +/// +public sealed class TagToIgnore +{ + /// + /// Gets or sets the value of the tag to ignore. I know, shocking... + /// + public string Value { get; set; } = string.Empty; + + /// + /// Gets or sets the Ignore Image property. When set to true, the image is ignored irrespective of any other + /// setting. + /// + public bool IgnoreImage { get; set; } + + /// + /// Returns this object in JSON format + /// + /// + /// This object serialized as a JSON object. + /// + public override string ToString() => + this.ToJson(); +} diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md b/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md new file mode 100644 index 0000000..16dacd1 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md @@ -0,0 +1 @@ +Update \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..5493a64 --- /dev/null +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs @@ -0,0 +1,48 @@ +using AStar.Dev.Admin.Api.Client.Sdk.AdminApi; +using AStar.Dev.Api.HealthChecks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Identity.Web; + +namespace AStar.Dev.Admin.Api.Client.Sdk; + +/// +/// +public static class ServiceCollectionExtensions +{ + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAdminApiClient(this IServiceCollection services, IConfiguration configuration, ILogger logger) + { + IConfigurationSection configurationSection = configuration.GetSection(AdminApiConfiguration.SectionLocation); + + _ = services.AddOptions() + .Bind(configurationSection) + .ValidateDataAnnotations() + .ValidateOnStart(); + + services.AddScoped(); + + // _ = services.AddHttpClient() + // .ConfigureHttpClient((serviceProvider, client) => + // { + // client.BaseAddress = serviceProvider + // .GetRequiredService>().Value + // .BaseUrl; + // + // client.DefaultRequestHeaders.Accept.Add( + // new + // MediaTypeWithQualityHeaderValue(MediaTypeNames.Application + // .Json)); + // }); + + services.AddDownstreamApi(nameof(AdminApiClient), configuration.GetSection(AdminApiConfiguration.SectionLocation)); + + return services; + } +} diff --git a/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj b/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj deleted file mode 100644 index 125f4c9..0000000 --- a/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net9.0 - enable - enable - - - diff --git a/src/AStar.Dev.Example.ClassLib/Class1.cs b/src/AStar.Dev.Example.ClassLib/Class1.cs deleted file mode 100644 index 27e9361..0000000 --- a/src/AStar.Dev.Example.ClassLib/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace AStar.Dev.Example.ClassLib; - -public class Class1 -{ - -} diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj new file mode 100644 index 0000000..125a474 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj @@ -0,0 +1,46 @@ + + + + net9.0 + enable + enable + false + AStar.Dev.Admin.Api.Client.Sdk + True + latest-recommended + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + True + 1701;1702;IDE0058; + + + + True + 1701;1702;IDE0058; + + + diff --git a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj b/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj deleted file mode 100644 index 6fbda4a..0000000 --- a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net9.0 - enable - enable - false - - - - - - - - - - - - - - - - - - diff --git a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs b/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs deleted file mode 100644 index 3928e91..0000000 --- a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace AStar.Dev.Example.ClassLib.Tests.Unit; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} From 3b719d22d5890273467edc0313dc3d1472321609 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Thu, 1 May 2025 04:14:37 +0100 Subject: [PATCH 2/6] Add the initial code from local --- nuget.config | 6 ++ .../AStar.Dev.Admin.Api.Client.Sdk.csproj | 21 +++-- .../AStar.Dev.Admin.Api.Client.Sdk.xml | 18 ++-- src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png | Bin 12513 -> 0 bytes .../AdminApi/AdminApiClient.cs | 83 ++++++++++++++---- .../AdminApi/AdminApiConfiguration.cs | 2 +- .../Constants.cs | 2 +- .../Models/ModelToIgnore.cs | 6 +- .../Models/ScrapeDirectories.cs | 2 +- .../Models/SearchCategory.cs | 2 +- .../Models/SearchConfiguration.cs | 2 +- .../Models/SiteConfiguration.cs | 2 +- .../Models/TagToIgnore.cs | 6 +- .../ServiceCollectionExtensions.cs | 12 ++- ...Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj | 6 ++ ...dminApiClientGetSiteConfigurationShould.cs | 41 +++++++++ .../AdminApiClientHealthChecksShould.cs | 54 ++++++++++++ ....ContainTheExpectedProperties.approved.txt | 6 ++ .../AdminApi/AdminApiConfigurationShould.cs | 12 +++ .../ConstantsShould.cs | 12 +++ .../Helpers/AdminApiClientFactory.cs | 30 +++++++ .../MockDeletionSuccessHttpMessageHandler.cs | 9 ++ ...RequestExceptionErrorHttpMessageHandler.cs | 7 ++ ...ckInternalServerErrorHttpMessageHandler.cs | 9 ++ .../MockSuccessHttpMessageHandler.cs | 28 ++++++ ...cessMessageWithValue0HttpMessageHandler.cs | 10 +++ ....ContainTheExpectedProperties.approved.txt | 3 + .../Models/ModelToIgnoreShould.cs | 12 +++ ....ContainTheExpectedProperties.approved.txt | 8 ++ .../Models/ScrapeDirectoriesShould.cs | 22 +++++ ....ContainTheExpectedProperties.approved.txt | 8 ++ .../Models/SearchCategoryShould.cs | 20 +++++ ....ContainTheExpectedProperties.approved.txt | 28 ++++++ .../Models/SearchConfigurationShould.cs | 33 +++++++ ....ContainTheExpectedProperties.approved.txt | 9 ++ .../Models/SiteConfigurationShould.cs | 23 +++++ ....ContainTheExpectedProperties.approved.txt | 4 + .../Models/TagToIgnoreShould.cs | 12 +++ .../ServiceCollectionExtensionsShould.cs | 12 +++ 39 files changed, 530 insertions(+), 52 deletions(-) create mode 100644 nuget.config delete mode 100644 src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ConstantsShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Helpers/AdminApiClientFactory.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockDeletionSuccessHttpMessageHandler.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockHttpRequestExceptionErrorHttpMessageHandler.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockInternalServerErrorHttpMessageHandler.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessMessageWithValue0HttpMessageHandler.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.ContainTheExpectedProperties.approved.txt create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..48a029b --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj index fa4f26b..8138ce4 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj @@ -5,7 +5,7 @@ AStar Developement, Jason Barden AStar Development AStar Developement, 2025 - Update. + This package contains the currently supported methods for interacting with the Admin API whilst abstracting the details.. $(AssemblyName).xml True True @@ -24,7 +24,7 @@ https://github.com/astar-development/ snupkg net9.0 - AStar.Dev.Update + AStar.Dev.Admin.Api.Client.Sdk 3a340cb0-60ee-4e38-93ac-0f8829c7193d 0.1.0 @@ -32,19 +32,24 @@ + - - - - - + + + + + + + + + - + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml index cb8ef56..018bfc3 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml @@ -9,13 +9,16 @@ The class. - + The class. - + + + + @@ -34,16 +37,14 @@ - + - + - The - - class containing the project-specific constants. + The class containing the project-specific constants. @@ -297,12 +298,11 @@ - + - diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.png deleted file mode 100644 index 8cac0a1d8136c82d5b582bdea30f24bf2e085e0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12513 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 MOs&qBpL2=*KT7L~lmGw# diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs index 1dd1303..74ce383 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs @@ -2,7 +2,8 @@ using System.Text.Json; using AStar.Dev.Admin.Api.Client.Sdk.Models; using AStar.Dev.Api.HealthChecks; -using Microsoft.Extensions.Logging; +using AStar.Dev.Functional.Extensions; +using AStar.Dev.Logging.Extensions; using Microsoft.Identity.Web; namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; @@ -10,20 +11,39 @@ namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; /// /// The class. /// -public sealed class AdminApiClient(HttpClient httpClient, ITokenAcquisition tokenAcquisitionService, ILogger logger) : IApiClient +public sealed class AdminApiClient(HttpClient httpClient, ITokenAcquisition tokenAcquisitionService, ILoggerAstar logger) : IApiClient { private static readonly JsonSerializerOptions JsonSerializerOptions = new(JsonSerializerDefaults.Web); /// public async Task GetHealthAsync(CancellationToken cancellationToken = default) { - logger.LogInformation("Checking the {ApiName} Health Status.", Constants.ApiName); + logger.LogHealthCheckStart(Constants.ApiName); try { - logger.LogInformation("Checking the {ApiName} Health Status.", Constants.ApiName); + var response = await httpClient.GetAsync("/health/ready", cancellationToken); - HttpResponseMessage response = await httpClient.GetAsync("/health/ready", cancellationToken); + return response.IsSuccessStatusCode + ? (await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(cancellationToken), JsonSerializerOptions, cancellationToken))! + : ReturnLoggedFailure(response); + } + catch (HttpRequestException ex) + { + logger.LogException(ex); + + return new() { Status = $"Could not get a response from the {Constants.ApiName}." }; + } + } + + /// + public async Task> GetHealthCheckAsync(CancellationToken cancellationToken = new ()) + { + logger.LogHealthCheckStart(Constants.ApiName); + + try + { + var response = await httpClient.GetAsync("/health/ready", cancellationToken); return response.IsSuccessStatusCode ? (await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(cancellationToken), JsonSerializerOptions, cancellationToken))! @@ -31,9 +51,9 @@ public async Task GetHealthAsync(CancellationToken cancell } catch (HttpRequestException ex) { - logger.LogError(500, ex, "Error: {ErrorMessage}", ex.Message); + logger.LogException(ex); - return new() { Status = $"Could not get a response from the {Constants.ApiName}.", }; + return new HealthStatusResponse { Status = $"Could not get a response from the {Constants.ApiName}." }; } } @@ -41,22 +61,53 @@ public async Task GetHealthAsync(CancellationToken cancell /// The GetSiteConfigurationAsync method will get the User Configuration. /// /// The Site Configuration - populated or empty - public async Task> GetSiteConfigurationAsync() + public async Task>> GetSiteConfigurationAsync() { - string token = await tokenAcquisitionService.GetAccessTokenForUserAsync(["api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write",]); + var token = await tokenAcquisitionService.GetAccessTokenForUserAsync(["api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write"]); - // logger.LogDebug("Token: {Token}", token); - httpClient.DefaultRequestHeaders.Authorization = new("Bearer", token); - HttpResponseMessage response = await httpClient.GetAsync("site-configurations?version=1.0"); + httpClient.AddBearerToken(token); + var response = await GetSafelyAsync>("site-configurations?version=1.0"); - return (await response.Content.ReadFromJsonAsync>())!; + return response.IsSuccess + ? response + : ReturnLoggedFailure("GetSiteConfiguration", response.Error); + } + + private async Task> GetSafelyAsync( string uri) + { + try + { + var response = await httpClient.GetAsync(uri); + + if(response.IsSuccessStatusCode) + { + return (await response.Content.ReadFromJsonAsync())!; + } + + logger.LogApiCallFailed("AStar.Dev.Admin.Api", uri, $"StatusCode: {response.StatusCode}, ResponseCode: {response.ReasonPhrase}"); + + return Result.Failure($"Call to {uri} failed with {(response.ReasonPhrase ?? response?.ReasonPhrase)}")!; + } + catch(Exception ex) + { + logger.LogException(ex); + + return Result.Failure(ex.Message)!; + } } private HealthStatusResponse ReturnLoggedFailure(HttpResponseMessage response) { - logger.LogInformation("The {ApiName} Health failed - {FailureReason}.", Constants.ApiName, response.ReasonPhrase); + logger.LogHealthCheckFailure(Constants.ApiName, response.ReasonPhrase ?? "Failure reason not known"); + + return new() { Status = $"Health Check failed - {response.ReasonPhrase}." }; + } + + private string ReturnLoggedFailure(string endpointName, string? reasonPhrase) + { + logger.LogApiCallFailed(Constants.ApiName, endpointName,reasonPhrase ?? "Failure reason not known"); - return new() { Status = $"Health Check failed - {response.ReasonPhrase}.", }; + return $"Call to {endpointName} failed - {reasonPhrase}."; } // @@ -134,4 +185,4 @@ private HealthStatusResponse ReturnLoggedFailure(HttpResponseMessage response) // .WebDeserialisationSettings) // : new UserConfiguration(); // } -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs index f836811..f591ca4 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiConfiguration.cs @@ -20,4 +20,4 @@ public sealed class AdminApiConfiguration : IApiConfiguration /// [Required] public required string[] Scopes { get; set; } -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs index bb683cb..2d4d1df 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Constants.cs @@ -9,4 +9,4 @@ public static class Constants /// Gets the Api Name. /// public const string ApiName = "AStar.Dev.Admin.Api"; -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs index 2e6317c..51eba12 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ModelToIgnore.cs @@ -18,6 +18,6 @@ public sealed class ModelToIgnore /// /// This object serialized as a JSON object. /// - public override string ToString() => - this.ToJson(); -} + public override string ToString() + => this.ToJson(); +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs index 5d2522f..a6df0d3 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/ScrapeDirectories.cs @@ -34,4 +34,4 @@ public sealed class ScrapeDirectories /// Gets or sets the default subdirectory name for the save - appended to the root directory. /// public string SubDirectoryName { get; set; } = string.Empty; -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs index 09f062d..c8d85af 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchCategory.cs @@ -34,4 +34,4 @@ public sealed class SearchCategory /// Gets or sets the Total Pages for the results. /// public int TotalPages { get; set; } -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs index 7f3bea6..7ad38b4 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SearchConfiguration.cs @@ -89,4 +89,4 @@ public sealed class SearchConfiguration /// Gets or sets the Top Wallpapers Starting Page Number. /// public int TopWallpapersStartingPageNumber { get; set; } -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs index e7a7666..bfba4e2 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/SiteConfiguration.cs @@ -39,4 +39,4 @@ public sealed class SiteConfiguration /// Gets or sets the Login URL /// public string LoginUrl { get; set; } = string.Empty; -} +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs index 94a3dfe..2b38a1a 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Models/TagToIgnore.cs @@ -24,6 +24,6 @@ public sealed class TagToIgnore /// /// This object serialized as a JSON object. /// - public override string ToString() => - this.ToJson(); -} + public override string ToString() + => this.ToJson(); +} \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs index 5493a64..911b464 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using AStar.Dev.Api.HealthChecks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Identity.Web; namespace AStar.Dev.Admin.Api.Client.Sdk; @@ -15,18 +14,17 @@ public static class ServiceCollectionExtensions /// /// /// - /// /// - public static IServiceCollection AddAdminApiClient(this IServiceCollection services, IConfiguration configuration, ILogger logger) + public static IServiceCollection AddAdminApiClient(this IServiceCollection services, IConfiguration configuration) { - IConfigurationSection configurationSection = configuration.GetSection(AdminApiConfiguration.SectionLocation); + var configurationSection = configuration.GetSection(AdminApiConfiguration.SectionLocation); _ = services.AddOptions() .Bind(configurationSection) .ValidateDataAnnotations() .ValidateOnStart(); - services.AddScoped(); + _ = services.AddScoped(); // _ = services.AddHttpClient() // .ConfigureHttpClient((serviceProvider, client) => @@ -41,8 +39,8 @@ public static IServiceCollection AddAdminApiClient(this IServiceCollection servi // .Json)); // }); - services.AddDownstreamApi(nameof(AdminApiClient), configuration.GetSection(AdminApiConfiguration.SectionLocation)); + _ = services.AddDownstreamApi(nameof(AdminApiClient), configuration.GetSection(AdminApiConfiguration.SectionLocation)); return services; } -} +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj index 125a474..517fc96 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj @@ -19,6 +19,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + @@ -33,6 +35,10 @@ + + + + True 1701;1702;IDE0058; diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs new file mode 100644 index 0000000..ef704db --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs @@ -0,0 +1,41 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientGetSiteConfigurationShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetSiteConfigurationAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetSiteConfigurationAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetSiteConfigurationAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("Health Check failed."); + + var response = await sut.GetSiteConfigurationAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetSiteConfigurationAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler(""); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetSiteConfigurationAsync(); + + response.IsSuccess.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs new file mode 100644 index 0000000..f810aa9 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs @@ -0,0 +1,54 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientHealthChecksShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetHealthAsyncWhenTheApiIsUnreachableVersion2() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetHealthCheckAsync(); + + response.IsSuccess.ShouldBeTrue(); + response.Value?.Description.ShouldBe("Unable to retrieve the description of the Health Status"); + response.Value?.Status.ShouldBe("Could not get a response from the AStar.Dev.Admin.Api."); + } + + [Fact] + public async Task ReturnExpectedFailureFromGetHealthAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetHealthAsync(); + + response.Status.ShouldBe("Could not get a response from the AStar.Dev.Admin.Api."); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetHealthAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("Health Check failed."); + + var response = await sut.GetHealthAsync(); + + response.Status.ShouldBe("Health Check failed - Internal Server Error."); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetHealthAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler(""); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetHealthAsync(); + + response.Status.ShouldBe("OK"); + } +} diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..b88b0fc --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,6 @@ +{ + "baseUrl": "https://www.example.com", + "scopes": [ + "Not Relevant Here" + ] +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.cs new file mode 100644 index 0000000..eca381e --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiConfigurationShould.cs @@ -0,0 +1,12 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiConfiguration))] +public class AdminApiConfigurationShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new AdminApiConfiguration { Scopes = ["Not Relevant Here"], BaseUrl = new ("https://www.example.com") }.ToJson().ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ConstantsShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ConstantsShould.cs new file mode 100644 index 0000000..d4def37 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ConstantsShould.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk; + +[TestSubject(typeof(Constants))] +public class ConstantsShould +{ + [Fact] + public void METHOD() + { + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Helpers/AdminApiClientFactory.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Helpers/AdminApiClientFactory.cs new file mode 100644 index 0000000..9ab37d7 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Helpers/AdminApiClientFactory.cs @@ -0,0 +1,30 @@ +using AStar.Dev.Admin.Api.Client.Sdk.AdminApi; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using AStar.Dev.Logging.Extensions; +using Microsoft.Identity.Web; +using NSubstitute; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Helpers; + +internal static class AdminApiClientFactory +{ + private const string IrrelevantUrl = "https://doesnot.matter.com"; + private static readonly ILoggerAstar DummyLogger = Substitute.For>(); + + public static AdminApiClient Create(HttpMessageHandler mockHttpMessageHandler) + { + var tokenAcquisitionServiceMock = Substitute.For(); + var httpClient = new HttpClient(mockHttpMessageHandler) { BaseAddress = new(IrrelevantUrl) }; + + return new(httpClient, tokenAcquisitionServiceMock, DummyLogger); + } + + public static AdminApiClient CreateInternalServerErrorClient(string errorMessage) + { + var tokenAcquisitionServiceMock = Substitute.For(); + var handler = new MockInternalServerErrorHttpMessageHandler(errorMessage); + var httpClient = new HttpClient(handler) { BaseAddress = new(IrrelevantUrl) }; + + return new(httpClient, tokenAcquisitionServiceMock, DummyLogger); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockDeletionSuccessHttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockDeletionSuccessHttpMessageHandler.cs new file mode 100644 index 0000000..6763575 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockDeletionSuccessHttpMessageHandler.cs @@ -0,0 +1,9 @@ +using System.Net; + +namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; + +public sealed class MockDeletionSuccessHttpMessageHandler : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Marked for deletion.") }); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockHttpRequestExceptionErrorHttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockHttpRequestExceptionErrorHttpMessageHandler.cs new file mode 100644 index 0000000..c95a8e3 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockHttpRequestExceptionErrorHttpMessageHandler.cs @@ -0,0 +1,7 @@ +namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; + +public sealed class MockHttpRequestExceptionErrorHttpMessageHandler : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + => throw new HttpRequestException(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockInternalServerErrorHttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockInternalServerErrorHttpMessageHandler.cs new file mode 100644 index 0000000..f991fc0 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockInternalServerErrorHttpMessageHandler.cs @@ -0,0 +1,9 @@ +using System.Net; + +namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; + +public sealed class MockInternalServerErrorHttpMessageHandler(string errorMessage) : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + => Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(errorMessage) }); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs new file mode 100644 index 0000000..10e4835 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs @@ -0,0 +1,28 @@ +using System.Net; +using System.Text.Json; +using AStar.Dev.Api.HealthChecks; + +namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; + +public sealed class MockSuccessHttpMessageHandler(string responseRequired) : HttpMessageHandler +{ + public int Counter { get; set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpContent content; + +#pragma warning disable IDE0045 // Convert to conditional expression + if (responseRequired == "Health") + { + content = new StringContent(JsonSerializer.Serialize(new HealthStatusResponse { Status = "OK" })); + } + else + { + content = new StringContent(JsonSerializer.Serialize(new HealthStatusResponse { Status = "NotSureYet" })); + } +#pragma warning restore IDE0045 // Convert to conditional expression + + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = content }); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessMessageWithValue0HttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessMessageWithValue0HttpMessageHandler.cs new file mode 100644 index 0000000..9246885 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessMessageWithValue0HttpMessageHandler.cs @@ -0,0 +1,10 @@ +using System.Net; + +namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; + +public sealed class MockSuccessMessageWithValue0HttpMessageHandler : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("0") }); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..90f112b --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,3 @@ +{ + "value": "Not Relevant Here" +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.cs new file mode 100644 index 0000000..ce30d3f --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ModelToIgnoreShould.cs @@ -0,0 +1,12 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(ModelToIgnore))] +public class ModelToIgnoreShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new ModelToIgnore { Value = "Not Relevant Here" }.ToJson().ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..c97e70e --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,8 @@ +{ + "id": 1, + "rootDirectory": "Root Directory Not Relevant Here", + "baseSaveDirectory": "Base Save Directory Not Relevant Here", + "baseDirectory": "Base Directory Not Relevant Here", + "baseDirectoryFamous": "Base Directory Famous Not Relevant Here", + "subDirectoryName": "Sub Directory Not Relevant Here" +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.cs new file mode 100644 index 0000000..7705ce3 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/ScrapeDirectoriesShould.cs @@ -0,0 +1,22 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(ScrapeDirectories))] +public class ScrapeDirectoriesShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new ScrapeDirectories + { + BaseSaveDirectory = "Base Save Directory Not Relevant Here", + Id = 1, + BaseDirectory = "Base Directory Not Relevant Here", + SubDirectoryName = "Sub Directory Not Relevant Here", + BaseDirectoryFamous = "Base Directory Famous Not Relevant Here", + RootDirectory = "Root Directory Not Relevant Here" + } + .ToJson() + .ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..b898cc1 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,8 @@ +{ + "id": "1", + "order": 3, + "name": "Name Not Relevant Here", + "lastKnownImageCount": 1, + "lastPageVisited": 2, + "totalPages": 4 +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.cs new file mode 100644 index 0000000..6f60312 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchCategoryShould.cs @@ -0,0 +1,20 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(SearchCategory))] +public class SearchCategoryShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new SearchCategory + { + Id = "1", + Name = "Name Not Relevant Here", + LastKnownImageCount = 1, + LastPageVisited = 2, + Order = 3, + TotalPages = 4 + }.ToJson().ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..a41daa3 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,28 @@ +{ + "id": 1, + "baseUrl": "https://not.relevant.here", + "loginUrl": "/not/relevant/here", + "searchCategories": [ + { + "id": "111", + "order": 2, + "name": "Name NotRelevant Here", + "lastKnownImageCount": 8, + "lastPageVisited": 9, + "totalPages": 7 + } + ], + "searchString": "", + "topWallpapers": "", + "searchStringPrefix": "", + "searchStringSuffix": "", + "subscriptions": "", + "imagePauseInSeconds": 5, + "startingPageNumber": 0, + "totalPages": 9, + "useHeadless": false, + "subscriptionsStartingPageNumber": 0, + "subscriptionsTotalPages": 0, + "topWallpapersTotalPages": 0, + "topWallpapersStartingPageNumber": 0 +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.cs new file mode 100644 index 0000000..7113966 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SearchConfigurationShould.cs @@ -0,0 +1,33 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(SearchConfiguration))] +public class SearchConfigurationShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new SearchConfiguration + { + BaseUrl = "https://not.relevant.here", + TotalPages = 9, + Id = 1, + ImagePauseInSeconds = 5, + LoginUrl = "/not/relevant/here", + SearchCategories = + [ + new() + { + Id = "111", + TotalPages = 7, + LastKnownImageCount = 8, + LastPageVisited = 9, + Name = "Name NotRelevant Here", + Order = 2 + } + ] + } + .ToJson() + .ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..c561002 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,9 @@ +{ + "id": 1, + "loginEmailAddress": "not@relevant.com", + "username": "username not relevant here", + "password": "Nope, not this!", + "siteConfigurationSlug": "slug-not-relevant-here", + "baseUrl": "https://not.relevant.here", + "loginUrl": "/login/not/relevant/here" +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.cs new file mode 100644 index 0000000..34c7d81 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/SiteConfigurationShould.cs @@ -0,0 +1,23 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(SiteConfiguration))] +public class SiteConfigurationShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new SiteConfiguration + { + BaseUrl = "https://not.relevant.here", + Id = 1, + LoginUrl = "/login/not/relevant/here", + LoginEmailAddress = "not@relevant.com", + Password = "Nope, not this!", + SiteConfigurationSlug = "slug-not-relevant-here", + Username = "username not relevant here" + } + .ToJson() + .ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.ContainTheExpectedProperties.approved.txt b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.ContainTheExpectedProperties.approved.txt new file mode 100644 index 0000000..17d8671 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.ContainTheExpectedProperties.approved.txt @@ -0,0 +1,4 @@ +{ + "value": "Value not relevant here", + "ignoreImage": true +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.cs new file mode 100644 index 0000000..3c61a75 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/Models/TagToIgnoreShould.cs @@ -0,0 +1,12 @@ +using AStar.Dev.Utilities; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.Models; + +[TestSubject(typeof(TagToIgnore))] +public class TagToIgnoreShould +{ + [Fact] + public void ContainTheExpectedProperties() + => new TagToIgnore { Value = "Value not relevant here", IgnoreImage = true }.ToJson().ShouldMatchApproved(); +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs new file mode 100644 index 0000000..c205f1c --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk; + +[TestSubject(typeof(ServiceCollectionExtensions))] +public class ServiceCollectionExtensionsShould +{ + [Fact] + public void METHOD() + { + } +} \ No newline at end of file From 6e51122ad99e6dfeeda4ae8f73c25531ffe84d4c Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Thu, 1 May 2025 17:44:55 +0100 Subject: [PATCH 3/6] First pass at migrating the code and tests --- .github/workflows/dotnet.yml | 90 +++++++--- AStar.Dev.Admin.Api.Client.Sdk.sln | 11 ++ .../AStar.Dev.Admin.Api.Client.Sdk.csproj | 2 +- .../AStar.Dev.Admin.Api.Client.Sdk.xml | 24 +++ .../AdminApi/AdminApiClient.cs | 163 +++++------------- src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md | 25 ++- .../ServiceCollectionExtensions.cs | 27 ++- ...Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj | 6 + .../AdminApiClientGetModelToIgnoreShould.cs | 41 +++++ ...dminApiClientGetScrapeDirectoriesShould.cs | 41 +++++ ...inApiClientGetSearchConfigurationShould.cs | 41 +++++ ...dminApiClientGetSiteConfigurationShould.cs | 4 +- .../AdminApiClientHealthChecksShould.cs | 6 +- .../AdminApiClientTagsToIgnoreShould.cs | 41 +++++ .../MockSuccessHttpMessageHandler.cs | 23 ++- .../ServiceCollectionExtensionsShould.cs | 34 +++- .../appsettings.json | 74 ++++++++ 17 files changed, 483 insertions(+), 170 deletions(-) create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetModelToIgnoreShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetScrapeDirectoriesShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSearchConfigurationShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientTagsToIgnoreShould.cs create mode 100644 test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/appsettings.json diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 0473985..4bd8f96 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,9 +1,7 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - -name: .NET +name: .NET - Build NuGet and Publish on: + workflow_dispatch: push: branches: [ "main" ] pull_request: @@ -11,25 +9,69 @@ on: jobs: build: - + name: Build and analyze runs-on: ubuntu-latest - + env: + ProjectName: 'AStar.Dev.Admin.Api.Client.Sdk' + RepositoryName: 'astar-dev-admin-api-client-sdk' steps: - - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.0.x - - - name: Delete nuget*.config files - run: rm -f nuget*.config - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --no-restore - - - name: Test - run: dotnet test --no-build --verbosity normal + - name: Set up JDK + uses: actions/setup-java@v4.4.0 + with: + java-version: 17 + distribution: 'zulu' + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.0.x + + - name: Checkout + uses: actions/checkout@v4.2.1 + with: + fetch-depth: 0 + + - name: Delete nuget*.config files + run: rm -f nuget*.config + + - name: Cache SonarCloud packages + uses: actions/cache@v4.2.3 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache SonarCloud scanner + id: cache-sonar-scanner + uses: actions/cache@v4.2.3 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + dotnet tool install --global dotnet-coverage + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.scanner.scanAll=false + dotnet build --configuration Release + dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + - name: Pack NuGet package + if: github.ref == 'refs/heads/main' + run: dotnet pack .\src\${{ env.ProjectName }}\${{ env.ProjectName }}.csproj + + - name: Push to NuGet + if: github.ref == 'refs/heads/main' + run: dotnet nuget push "**\${{ env.ProjectName }}.*.nupkg" --api-key ${{secrets.nuget_api_key}} --skip-duplicate --source https://api.nuget.org/v3/index.json diff --git a/AStar.Dev.Admin.Api.Client.Sdk.sln b/AStar.Dev.Admin.Api.Client.Sdk.sln index bb4c3b8..a9dc473 100644 --- a/AStar.Dev.Admin.Api.Client.Sdk.sln +++ b/AStar.Dev.Admin.Api.Client.Sdk.sln @@ -11,6 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Admin.Api.Client. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit", "test\AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit\AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj", "{20915888-5AEA-4918-8DE4-FBE77EFCC758}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{94A53025-23C4-4B9D-B015-B7CDBD1E2275}" + ProjectSection(SolutionItems) = preProject + .github\dependabot.yml = .github\dependabot.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{790D531F-FFB9-4C8C-A861-C469CDF5DB9D}" + ProjectSection(SolutionItems) = preProject + .github\workflows\dotnet.yml = .github\workflows\dotnet.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -22,6 +32,7 @@ Global GlobalSection(NestedProjects) = preSolution {1A33B6F9-9126-43D5-962B-6BA2DBE5B052} = {F881F2A5-6B1D-4E4F-A698-C3D5E760E509} {20915888-5AEA-4918-8DE4-FBE77EFCC758} = {F205434D-6BE3-414B-A17D-A12F8E78C58F} + {790D531F-FFB9-4C8C-A861-C469CDF5DB9D} = {94A53025-23C4-4B9D-B015-B7CDBD1E2275} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1A33B6F9-9126-43D5-962B-6BA2DBE5B052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj index 8138ce4..a48272c 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.csproj @@ -40,7 +40,7 @@ - + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml index 018bfc3..54d3e7e 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AStar.Dev.Admin.Api.Client.Sdk.xml @@ -26,6 +26,30 @@ The Site Configuration - populated or empty + + + The GetModelsToIgnoreAsync method will get the models to ignore. + + A collection of 0 or more models to ignore + + + + The GetScrapeDirectoriesAsync method will get the Scrape Directories. + + The Scrape Directories - populated or empty + + + + The GetSearchConfigurationAsync method will get the Search Configuration. + + The Search Configuration - populated or empty + + + + The GetTagsToIgnoreAsync method will get the Tags to Ignore. + + A collection of 0 or more Tags to Ignore + The class containing the current configuration settings. diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs index 74ce383..5ab246f 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/AdminApi/AdminApiClient.cs @@ -4,6 +4,8 @@ using AStar.Dev.Api.HealthChecks; using AStar.Dev.Functional.Extensions; using AStar.Dev.Logging.Extensions; +using AStar.Dev.Technical.Debt.Reporting; +using AStar.Dev.Utilities; using Microsoft.Identity.Web; namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; @@ -16,7 +18,7 @@ public sealed class AdminApiClient(HttpClient httpClient, ITokenAcquisition toke private static readonly JsonSerializerOptions JsonSerializerOptions = new(JsonSerializerDefaults.Web); /// - public async Task GetHealthAsync(CancellationToken cancellationToken = default) + [Refactor(1,1,"Remove from the Interface")]public async Task GetHealthAsync(CancellationToken cancellationToken = default) { logger.LogHealthCheckStart(Constants.ApiName); @@ -26,7 +28,7 @@ public async Task GetHealthAsync(CancellationToken cancell return response.IsSuccessStatusCode ? (await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(cancellationToken), JsonSerializerOptions, cancellationToken))! - : ReturnLoggedFailure(response); + : logger.ReturnLoggedFailure(response, Constants.ApiName); } catch (HttpRequestException ex) { @@ -38,55 +40,60 @@ public async Task GetHealthAsync(CancellationToken cancell /// public async Task> GetHealthCheckAsync(CancellationToken cancellationToken = new ()) - { - logger.LogHealthCheckStart(Constants.ApiName); - - try - { - var response = await httpClient.GetAsync("/health/ready", cancellationToken); - - return response.IsSuccessStatusCode - ? (await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(cancellationToken), JsonSerializerOptions, cancellationToken))! - : ReturnLoggedFailure(response); - } - catch (HttpRequestException ex) - { - logger.LogException(ex); - - return new HealthStatusResponse { Status = $"Could not get a response from the {Constants.ApiName}." }; - } - } + => await GetSafelyAsync("/health/ready?version=1.0"); /// /// The GetSiteConfigurationAsync method will get the User Configuration. /// /// The Site Configuration - populated or empty public async Task>> GetSiteConfigurationAsync() - { - var token = await tokenAcquisitionService.GetAccessTokenForUserAsync(["api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write"]); + => await GetSafelyAsync>("site-configurations?version=1.0"); - httpClient.AddBearerToken(token); - var response = await GetSafelyAsync>("site-configurations?version=1.0"); + /// + /// The GetModelsToIgnoreAsync method will get the models to ignore. + /// + /// A collection of 0 or more models to ignore + public async Task>> GetModelsToIgnoreAsync() + => await GetSafelyAsync>("models-to-ignore?version=1"); - return response.IsSuccess - ? response - : ReturnLoggedFailure("GetSiteConfiguration", response.Error); - } + /// + /// The GetScrapeDirectoriesAsync method will get the Scrape Directories. + /// + /// The Scrape Directories - populated or empty + public async Task> GetScrapeDirectoriesAsync() + => await GetSafelyAsync("scrape-directories?version=1"); + + /// + /// The GetSearchConfigurationAsync method will get the Search Configuration. + /// + /// The Search Configuration - populated or empty + public async Task> GetSearchConfigurationAsync() + => await GetSafelyAsync("search-configuration?version=1"); + + /// + /// The GetTagsToIgnoreAsync method will get the Tags to Ignore. + /// + /// A collection of 0 or more Tags to Ignore + public async Task>> GetTagsToIgnoreAsync() + => await GetSafelyAsync>("search-configuration?version=1"); private async Task> GetSafelyAsync( string uri) { try { + logger.LogApiCallStart(Constants.ApiName, uri); + var token = await tokenAcquisitionService.GetAccessTokenForUserAsync(["api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write"]); + + _ = httpClient.AddBearerToken(token); var response = await httpClient.GetAsync(uri); - if(response.IsSuccessStatusCode) + if (!response.IsSuccessStatusCode) { - return (await response.Content.ReadFromJsonAsync())!; + return logger.ReturnLoggedFailure( Constants.ApiName, uri, response.ReasonPhrase ?? response.StatusCode.ToString()); } - logger.LogApiCallFailed("AStar.Dev.Admin.Api", uri, $"StatusCode: {response.StatusCode}, ResponseCode: {response.ReasonPhrase}"); - - return Result.Failure($"Call to {uri} failed with {(response.ReasonPhrase ?? response?.ReasonPhrase)}")!; + var result = (await response.Content.ReadFromJsonAsync(Utilities.Constants.WebDeserialisationSettings))!; + return logger.ReturnLoggedSuccess(result, Constants.ApiName, "uri"); } catch(Exception ex) { @@ -95,94 +102,4 @@ private async Task> GetSafelyAsync( string return Result.Failure(ex.Message)!; } } - - private HealthStatusResponse ReturnLoggedFailure(HttpResponseMessage response) - { - logger.LogHealthCheckFailure(Constants.ApiName, response.ReasonPhrase ?? "Failure reason not known"); - - return new() { Status = $"Health Check failed - {response.ReasonPhrase}." }; - } - - private string ReturnLoggedFailure(string endpointName, string? reasonPhrase) - { - logger.LogApiCallFailed(Constants.ApiName, endpointName,reasonPhrase ?? "Failure reason not known"); - - return $"Call to {endpointName} failed - {reasonPhrase}."; - } - - // - // /// - // /// The GetModelsToIgnoreAsync method will get the models to ignore. - // /// - // /// A collection of 0 or more models to ignore - // public async Task> GetModelsToIgnoreAsync() - // { - // logger.LogInformation("Getting the models-to-ignore."); - // var response = await httpClient.GetAsync("models-to-ignore?version=1"); - // - // return response.IsSuccessStatusCode - // ? (await response.Content.ReadAsStringAsync()).FromJson>(Utilities.Constants - // .WebDeserialisationSettings) - // : []; - // } - // - // /// - // /// The GetScrapeDirectoriesAsync method will get the Scrape Directories. - // /// - // /// The Scrape Directories - populated or empty - // public async Task GetScrapeDirectoriesAsync() - // { - // logger.LogInformation("Getting the scrape-directories."); - // var response = await httpClient.GetAsync("scrape-directories?version=1"); - // - // return response.IsSuccessStatusCode - // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants - // .WebDeserialisationSettings) - // : new ScrapeDirectories(); - // } - // - // /// - // /// The GetSearchConfigurationAsync method will get the Search Configuration. - // /// - // /// The Search Configuration - populated or empty - // public async Task GetSearchConfigurationAsync() - // { - // logger.LogInformation("Getting the search-configuration."); - // var response = await httpClient.GetAsync("search-configuration?version=1"); - // - // return response.IsSuccessStatusCode - // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants - // .WebDeserialisationSettings) - // : new SearchConfiguration(); - // } - // - // /// - // /// The GetTagsToIgnoreAsync method will get the Tags to Ignore. - // /// - // /// A collection of 0 or more Tags to Ignore - // public async Task> GetTagsToIgnoreAsync() - // { - // logger.LogInformation("Getting the Tags-to-ignore."); - // var response = await httpClient.GetAsync("tags-to-ignore?version=1"); - // - // return response.IsSuccessStatusCode - // ? (await response.Content.ReadAsStringAsync()).FromJson>(Utilities.Constants - // .WebDeserialisationSettings) - // : []; - // } - // - // /// - // /// The GetUserConfigurationAsync method will get the User Configuration. - // /// - // /// The User Configuration - populated or empty - // public async Task GetUserConfigurationAsync() - // { - // logger.LogInformation("Getting the User Configuration."); - // var response = await httpClient.GetAsync("user-configuration?version=1"); - // - // return response.IsSuccessStatusCode - // ? (await response.Content.ReadAsStringAsync()).FromJson(Utilities.Constants - // .WebDeserialisationSettings) - // : new UserConfiguration(); - // } } \ No newline at end of file diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md b/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md index 16dacd1..10a241f 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/Readme.md @@ -1 +1,24 @@ -Update \ No newline at end of file +# AStar.Dev.Admin.Api.Client.Sdk + +This NuGet package, as the name suggests, contains the Client SDK for accessing the AStar.Dev.Admin.Api + +Methods include calling the healthcheck endpoint and various configuration-related endpoints + +## GitHub build + +[![.NET - Build NuGet and Publish](https://github.com/astar-development/astar-dev-admin-api-client-sdk/actions/workflows/dotnet.yml/badge.svg)](https://github.com/astar-development/astar-dev-admin-api-client-sdk/actions/workflows/dotnet.yml) + +## SonarCloud Analysis Results + +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=bugs)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=coverage)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=jbarden_astar-dev-admin-api-client-sdk&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=jbarden_astar-dev-admin-api-client-sdk) + diff --git a/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs index 911b464..a8a08d9 100644 --- a/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs +++ b/src/AStar.Dev.Admin.Api.Client.Sdk/ServiceCollectionExtensions.cs @@ -1,7 +1,9 @@ +using System.Net.Mime; using AStar.Dev.Admin.Api.Client.Sdk.AdminApi; using AStar.Dev.Api.HealthChecks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.Identity.Web; namespace AStar.Dev.Admin.Api.Client.Sdk; @@ -26,20 +28,17 @@ public static IServiceCollection AddAdminApiClient(this IServiceCollection servi _ = services.AddScoped(); - // _ = services.AddHttpClient() - // .ConfigureHttpClient((serviceProvider, client) => - // { - // client.BaseAddress = serviceProvider - // .GetRequiredService>().Value - // .BaseUrl; - // - // client.DefaultRequestHeaders.Accept.Add( - // new - // MediaTypeWithQualityHeaderValue(MediaTypeNames.Application - // .Json)); - // }); - - _ = services.AddDownstreamApi(nameof(AdminApiClient), configuration.GetSection(AdminApiConfiguration.SectionLocation)); + _ = services.AddHttpClient() + .ConfigureHttpClient((serviceProvider, client) => + { + client.BaseAddress = serviceProvider + .GetRequiredService>().Value + .BaseUrl; + + client.DefaultRequestHeaders.Accept.Add(new(MediaTypeNames.Application.Json)); + }); + + // _ = services.AddDownstreamApi(nameof(AdminApiClient), configuration.GetSection(AdminApiConfiguration.SectionLocation)); return services; } diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj index 517fc96..7ef1293 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit.csproj @@ -39,6 +39,12 @@ + + + PreserveNewest + + + True 1701;1702;IDE0058; diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetModelToIgnoreShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetModelToIgnoreShould.cs new file mode 100644 index 0000000..df5a011 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetModelToIgnoreShould.cs @@ -0,0 +1,41 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientGetModelToIgnoreShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetModelsToIgnoreAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetModelsToIgnoreAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetModelsToIgnoreAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("ModelsToIgnore"); + + var response = await sut.GetModelsToIgnoreAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetModelsToIgnoreAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler("ModelsToIgnore"); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetModelsToIgnoreAsync(); + + response.IsSuccess.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetScrapeDirectoriesShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetScrapeDirectoriesShould.cs new file mode 100644 index 0000000..5995e63 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetScrapeDirectoriesShould.cs @@ -0,0 +1,41 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientGetScrapeDirectoriesShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetScrapeDirectoriesAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetScrapeDirectoriesAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetScrapeDirectoriesAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("ScrapeDirectories"); + + var response = await sut.GetScrapeDirectoriesAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetScrapeDirectoriesAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler("ScrapeDirectories"); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetScrapeDirectoriesAsync(); + + response.IsSuccess.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSearchConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSearchConfigurationShould.cs new file mode 100644 index 0000000..12c8546 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSearchConfigurationShould.cs @@ -0,0 +1,41 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientSearchConfigurationShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetSearchConfigurationAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetSearchConfigurationAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetSearchConfigurationAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("SearchConfiguration"); + + var response = await sut.GetSearchConfigurationAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetSearchConfigurationAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler("SearchConfigurations"); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetSearchConfigurationAsync(); + + response.IsSuccess.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs index ef704db..834c5d6 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientGetSiteConfigurationShould.cs @@ -21,7 +21,7 @@ public async Task ReturnExpectedFailureFromGetSiteConfigurationAsyncWhenTheApiIs [Fact] public async Task ReturnExpectedFailureMessageFromGetSiteConfigurationAsyncWhenCheckFails() { - var sut = AdminApiClientFactory.CreateInternalServerErrorClient("Health Check failed."); + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("SiteConfiguration"); var response = await sut.GetSiteConfigurationAsync(); @@ -31,7 +31,7 @@ public async Task ReturnExpectedFailureMessageFromGetSiteConfigurationAsyncWhenC [Fact] public async Task ReturnExpectedMessageFromGetSiteConfigurationAsyncWhenCheckSucceeds() { - var handler = new MockSuccessHttpMessageHandler(""); + var handler = new MockSuccessHttpMessageHandler("SiteConfigurations"); var sut = AdminApiClientFactory.Create(handler); var response = await sut.GetSiteConfigurationAsync(); diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs index f810aa9..0a761fd 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientHealthChecksShould.cs @@ -15,7 +15,7 @@ public async Task ReturnExpectedFailureFromGetHealthAsyncWhenTheApiIsUnreachable var response = await sut.GetHealthCheckAsync(); - response.IsSuccess.ShouldBeTrue(); + response.IsFailure.ShouldBeTrue(); response.Value?.Description.ShouldBe("Unable to retrieve the description of the Health Status"); response.Value?.Status.ShouldBe("Could not get a response from the AStar.Dev.Admin.Api."); } @@ -44,11 +44,11 @@ public async Task ReturnExpectedFailureMessageFromGetHealthAsyncWhenCheckFails() [Fact] public async Task ReturnExpectedMessageFromGetHealthAsyncWhenCheckSucceeds() { - var handler = new MockSuccessHttpMessageHandler(""); + var handler = new MockSuccessHttpMessageHandler("Health"); var sut = AdminApiClientFactory.Create(handler); var response = await sut.GetHealthAsync(); response.Status.ShouldBe("OK"); } -} +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientTagsToIgnoreShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientTagsToIgnoreShould.cs new file mode 100644 index 0000000..ee70c13 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/AdminApi/AdminApiClientTagsToIgnoreShould.cs @@ -0,0 +1,41 @@ +using AStar.Dev.Admin.Api.Client.Sdk.Helpers; +using AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; +using JetBrains.Annotations; + +namespace AStar.Dev.Admin.Api.Client.Sdk.AdminApi; + +[TestSubject(typeof(AdminApiClient))] +public class AdminApiClientTagsToIgnoreShould +{ + [Fact] + public async Task ReturnExpectedFailureFromGetTagsToIgnoreAsyncWhenTheApiIsUnreachable() + { + var handler = new MockHttpRequestExceptionErrorHttpMessageHandler(); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetTagsToIgnoreAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedFailureMessageFromGetTagsToIgnoreAsyncWhenCheckFails() + { + var sut = AdminApiClientFactory.CreateInternalServerErrorClient("tags-to-ignore"); + + var response = await sut.GetTagsToIgnoreAsync(); + + response.IsFailure.ShouldBeTrue(); + } + + [Fact] + public async Task ReturnExpectedMessageFromGetTagsToIgnoreAsyncWhenCheckSucceeds() + { + var handler = new MockSuccessHttpMessageHandler("TagToIgnore"); + var sut = AdminApiClientFactory.Create(handler); + + var response = await sut.GetTagsToIgnoreAsync(); + + response.IsSuccess.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs index 10e4835..6dcb797 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/MockMessageHandlers/MockSuccessHttpMessageHandler.cs @@ -1,5 +1,6 @@ using System.Net; using System.Text.Json; +using AStar.Dev.Admin.Api.Client.Sdk.Models; using AStar.Dev.Api.HealthChecks; namespace AStar.Dev.Admin.Api.Client.Sdk.MockMessageHandlers; @@ -13,7 +14,27 @@ protected override Task SendAsync(HttpRequestMessage reques HttpContent content; #pragma warning disable IDE0045 // Convert to conditional expression - if (responseRequired == "Health") + if(responseRequired == "SiteConfigurations") + { + content = new StringContent(JsonSerializer.Serialize(new List { new () } )); + } + else if(responseRequired == "SiteConfiguration") + { + content = new StringContent(JsonSerializer.Serialize(new SiteConfiguration())); + } + else if(responseRequired == "SearchConfiguration") + { + content = new StringContent(JsonSerializer.Serialize(new List { new () } )); + } + else if(responseRequired == "TagToIgnore") + { + content = new StringContent(JsonSerializer.Serialize(new List { new () } )); + } + else if(responseRequired == "ModelsToIgnore") + { + content = new StringContent(JsonSerializer.Serialize(new List { new () } )); + } + else if (responseRequired == "Health") { content = new StringContent(JsonSerializer.Serialize(new HealthStatusResponse { Status = "OK" })); } diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs index c205f1c..a529170 100644 --- a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/ServiceCollectionExtensionsShould.cs @@ -1,12 +1,44 @@ +using AStar.Dev.Admin.Api.Client.Sdk.AdminApi; +using AStar.Dev.Logging.Extensions; using JetBrains.Annotations; +using Microsoft.ApplicationInsights.Channel; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Microsoft.Identity.Web; +using NSubstitute; namespace AStar.Dev.Admin.Api.Client.Sdk; [TestSubject(typeof(ServiceCollectionExtensions))] public class ServiceCollectionExtensionsShould { + private readonly ServiceProvider serviceProvider; + + public ServiceCollectionExtensionsShould() + { + var configurationManager = new ConfigurationManager(); + configurationManager.AddJsonFile("appsettings.json"); + var serviceCollection = new ServiceCollection(); + var tokenAcquisitionServiceMock = Substitute.For(); + serviceCollection.AddSingleton(tokenAcquisitionServiceMock); + var loggerMock = Substitute.For>(); + serviceCollection.AddSingleton(loggerMock); + + serviceCollection.AddAdminApiClient(configurationManager); + + serviceProvider = serviceCollection.BuildServiceProvider(); + } [Fact] - public void METHOD() + public void AddTheExpectedServices() { + serviceProvider.GetService>().ShouldNotBeNull(); + serviceProvider.GetService().ShouldNotBeNull(); } + + [Fact] + public void AddTheExpectedHttpClient() + => serviceProvider.GetService().ShouldNotBeNull(); } \ No newline at end of file diff --git a/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/appsettings.json b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/appsettings.json new file mode 100644 index 0000000..ba47ed0 --- /dev/null +++ b/test/AStar.Dev.Admin.Api.Client.Sdk.Tests.Unit/appsettings.json @@ -0,0 +1,74 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "apiUsageConfiguration": { + "hostName": "astar-api-usage", + "userName": "user", + "password": null, + "queueName": "usage" + }, + "AllowedHosts": "*", + "AzureAd": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "jasonbardenoutlook.onmicrosoft.com", + "TenantId": "bb7d94aa-36a9-4a59-a0c1-54a757c47ddf", + "ClientId": "2ca26585-5929-4aae-86a7-a00c3fc2d061", + "ClientSecret": "This is a secret... LOL", + "CallbackPath": "/signin-oidc" + }, + "TodoList": { + "Scopes": [ + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Read", + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write" + ], + "BaseUrl": "http://todolistservice/", + "RelativePath": "api/todolist" + }, + "GraphApi": { + "BaseUrl": "https://graph.microsoft.com/v1.0/me", + "Scopes": [ + "user.read" + ] + }, + "apiConfiguration": { + "adminApiConfiguration": { + "Scopes": [ + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Read", + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write" + ], + "baseUrl": "http://astar.dev.admin.api/" + }, + "filesApiConfiguration": { + "Scopes": [ + "api://54861ab2-fdb0-4e18-a073-c90e7bf9f0c5/ToDoList.Write", + "api://54861ab2-fdb0-4e18-a073-c90e7bf9f0c5/ToDoList.Read" + ], + "baseUrl": "http://astar.dev.files.api/" + }, + "imagesApiConfiguration": { + "Scopes": [ + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Read", + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write" + ], + "baseUrl": "http://host.docker.internal:5062/" + }, + "usageApiConfiguration": { + "Scopes": [ + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Read", + "api://2ca26585-5929-4aae-86a7-a00c3fc2d061/ToDoList.Write" + ], + "baseUrl": "http://astar.dev.usage.logger/" + } + }, + "applicationConfiguration": { + "paginationPageDefaultPreAndPostCount": 5 + }, + "ApplicationInsights": { + "ConnectionString": "InstrumentationKey=Update" + } +} From 10ee847721db0261b57c6aa69016ffded7e138b4 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Thu, 1 May 2025 17:48:07 +0100 Subject: [PATCH 4/6] Revert to run on windows-latest --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 4bd8f96..d6f7402 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -10,7 +10,7 @@ on: jobs: build: name: Build and analyze - runs-on: ubuntu-latest + runs-on: windows-latest env: ProjectName: 'AStar.Dev.Admin.Api.Client.Sdk' RepositoryName: 'astar-dev-admin-api-client-sdk' From 3ef8c33201d3d64d45355a24575a151983416074 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Thu, 1 May 2025 17:50:10 +0100 Subject: [PATCH 5/6] Remove the pointless nuget*config delete --- .github/workflows/dotnet.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d6f7402..c8617dc 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -31,9 +31,6 @@ jobs: with: fetch-depth: 0 - - name: Delete nuget*.config files - run: rm -f nuget*.config - - name: Cache SonarCloud packages uses: actions/cache@v4.2.3 with: From cc1e24b7caa9c59fce91ceba575b1ceac3ca6771 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Thu, 1 May 2025 17:54:25 +0100 Subject: [PATCH 6/6] Delete nuget.config as not required --- nuget.config | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 nuget.config diff --git a/nuget.config b/nuget.config deleted file mode 100644 index 48a029b..0000000 --- a/nuget.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - -