From 61f2182adc1438099a183115c077b763134ac75d Mon Sep 17 00:00:00 2001 From: "Ethan E." Date: Mon, 25 Aug 2025 22:32:51 -0500 Subject: [PATCH 1/4] Move Repo to Fork --- EcommerceAPI.GoldRino456/.gitattributes | 2 + EcommerceAPI.GoldRino456/.gitignore | 401 ++++++++++++ .../EcomAPIDemo.postman_collection.json | 570 ++++++++++++++++++ .../EcommerceAPIDemo/Controllers/Filters.cs | 18 + .../Controllers/GamesController.cs | 140 +++++ .../Controllers/Pagination.cs | 31 + .../Controllers/SalesController.cs | 114 ++++ .../EcommerceAPIDemo/Data/DTOs/CategoryDto.cs | 6 + .../Data/DTOs/GameProductDtos.cs | 26 + .../Data/DTOs/GameProductResponseDto.cs | 16 + .../EcommerceAPIDemo/Data/DTOs/SaleDto.cs | 13 + .../Data/DTOs/SaleResponseDto.cs | 19 + .../Data/Models/GameCategory.cs | 7 + .../Data/Models/GameProduct.cs | 16 + .../EcommerceAPIDemo/Data/Models/Sale.cs | 25 + .../EcommerceAPIDemo/Data/SalesDbContext.cs | 11 + .../EcommerceAPIDemo/EcommerceAPIDemo.csproj | 23 + .../EcommerceAPIDemo/EcommerceAPIDemo.http | 6 + .../EcommerceAPIDemo/EcommerceAPIDemo.sln | 25 + .../20250810212441_InitialCreate.Designer.cs | 177 ++++++ .../20250810212441_InitialCreate.cs | 142 +++++ ...250810212540_UpdateGameProduct.Designer.cs | 177 ++++++ .../20250810212540_UpdateGameProduct.cs | 34 ++ ...220249_AddedMoreSpecificFields.Designer.cs | 189 ++++++ .../20250810220249_AddedMoreSpecificFields.cs | 63 ++ ...20250810224235_EditGameProduct.Designer.cs | 186 ++++++ .../20250810224235_EditGameProduct.cs | 29 + ...19210036_MinorSalesModelUpdate.Designer.cs | 192 ++++++ .../20250819210036_MinorSalesModelUpdate.cs | 40 ++ .../20250823224045_CategoryChange.Designer.cs | 179 ++++++ .../20250823224045_CategoryChange.cs | 80 +++ ...5121_DBTestingMtoMRelationship.Designer.cs | 192 ++++++ ...0250823225121_DBTestingMtoMRelationship.cs | 80 +++ ...823230627_GameCategoriesUpdate.Designer.cs | 179 ++++++ .../20250823230627_GameCategoriesUpdate.cs | 80 +++ .../Migrations/SalesDbContextModelSnapshot.cs | 176 ++++++ .../EcommerceAPIDemo/Program.cs | 26 + .../Properties/launchSettings.json | 41 ++ .../EcommerceAPIDemo/Services/GamesService.cs | 238 ++++++++ .../EcommerceAPIDemo/Services/SalesService.cs | 174 ++++++ .../appsettings.Development.json | 8 + .../EcommerceAPIDemo/appsettings.json | 19 + 42 files changed, 4170 insertions(+) create mode 100644 EcommerceAPI.GoldRino456/.gitattributes create mode 100644 EcommerceAPI.GoldRino456/.gitignore create mode 100644 EcommerceAPI.GoldRino456/EcomAPIDemo.postman_collection.json create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Filters.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/GamesController.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Pagination.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/SalesController.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/CategoryDto.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductDtos.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductResponseDto.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleResponseDto.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameCategory.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameProduct.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/Sale.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/SalesDbContext.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.csproj create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.http create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.sln create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.Designer.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/SalesDbContextModelSnapshot.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Program.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Properties/launchSettings.json create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/GamesService.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.Development.json create mode 100644 EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.json diff --git a/EcommerceAPI.GoldRino456/.gitattributes b/EcommerceAPI.GoldRino456/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/EcommerceAPI.GoldRino456/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/EcommerceAPI.GoldRino456/.gitignore b/EcommerceAPI.GoldRino456/.gitignore new file mode 100644 index 00000000..93df31bc --- /dev/null +++ b/EcommerceAPI.GoldRino456/.gitignore @@ -0,0 +1,401 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/EcommerceAPI.GoldRino456/EcomAPIDemo.postman_collection.json b/EcommerceAPI.GoldRino456/EcomAPIDemo.postman_collection.json new file mode 100644 index 00000000..b4e42764 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcomAPIDemo.postman_collection.json @@ -0,0 +1,570 @@ +{ + "info": { + "_postman_id": "638987f7-5aab-44df-8d42-ea9d7d8226f5", + "name": "EcomAPIDemo", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "47473440" + }, + "item": [ + { + "name": "Categories", + "item": [ + { + "name": "All Categories", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games/categories?pageNumber=1&pageSize=50", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + "categories" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "50" + } + ] + } + }, + "response": [] + }, + { + "name": "Category By Id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/games/categories/:categoryId", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + "categories", + ":categoryId" + ], + "variable": [ + { + "key": "categoryId", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Category", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"{{$randomProductName}}\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games/categories", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Category By Id", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"API CATEGORY TEST PUT 1\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games/categories/:categoryId", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + "categories", + ":categoryId" + ], + "variable": [ + { + "key": "categoryId", + "value": "1" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Games", + "item": [ + { + "name": "All Games", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games?pageNumber=1&pageSize=25", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "25" + }, + { + "key": "categoryId", + "value": "1", + "disabled": true + }, + { + "key": "minPrice", + "value": "0.00", + "disabled": true + }, + { + "key": "maxPrice", + "value": "85.00", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Game By Id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/games/:gameId", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + ":gameId" + ], + "variable": [ + { + "key": "gameId", + "value": "14" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Game", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "try {\r", + " const response = await pm.sendRequest({\r", + " url: \"https://localhost:8080/api/games/categories?pageNumber=1&pageSize=50\",\r", + " method: \"GET\"\r", + " });\r", + "\r", + " console.log(response.json());\r", + "\r", + " if(response.json().totalRecords == 0)\r", + " {\r", + " pm.variables.set(\"randomCategories\", JSON.stringify([]));\r", + " return;\r", + " }\r", + "\r", + " const categories = response.json().data;\r", + " const ids = categories.map(c => c.id);\r", + "\r", + " const arrLength = Math.floor(Math.random() * 5);\r", + " const shuffledList = ids.sort(() => 0.5 - Math.random());\r", + " const randomIds = shuffledList.slice(0, arrLength);\r", + "\r", + " pm.variables.set(\"randomCategories\", JSON.stringify(randomIds));\r", + "\r", + "} catch (err) {\r", + " console.error(err);\r", + "}" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"{{$randomFileName}}\",\r\n \"description\": \"{{$randomLoremSentences}}\",\r\n \"gameCategoryIds\": {{randomCategories}},\r\n \"developer\": \"{{$randomCompanyName}}\",\r\n \"publisher\": \"{{$randomCompanyName}}\",\r\n \"releaseDate\": \"{{$isoTimestamp}}\",\r\n \"price\": {{$randomPrice}},\r\n \"fileSize\": {{$randomPrice}},\r\n \"systemRequirements\": \"{{$randomLoremSentences}}\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games" + ] + } + }, + "response": [] + }, + { + "name": "Game By Id", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"API TEST GAME PUT\",\r\n \"description\": \"This should be updated.\",\r\n \"gameCategoryIds\": [\r\n 29, 12, 15\r\n ],\r\n \"developer\": \"DEMO\",\r\n \"publisher\": \"TEST\",\r\n \"releaseDate\": \"2023-12-22T20:26:18.524Z\",\r\n \"fileSize\": 33.5,\r\n \"systemRequirements\": \"A really, really strong pc.\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/games/:gameId", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "games", + ":gameId" + ], + "variable": [ + { + "key": "gameId", + "value": "15" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Sales", + "item": [ + { + "name": "All Sales", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/sales?pageNumber=1&pageSize=10", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + }, + { + "key": "gameProductId", + "value": "1", + "disabled": true + }, + { + "key": "lastFourOfPaymentCard", + "value": "1234", + "disabled": true + }, + { + "key": "transactionDate", + "value": "{{$randomDateRecent}}", + "disabled": true + }, + { + "key": "minTransactionValue", + "value": "0", + "disabled": true + }, + { + "key": "maxTransactionValue", + "value": "100", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Sale By Id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/sales/:saleId", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales", + ":saleId" + ], + "variable": [ + { + "key": "saleId", + "value": "4" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Sale", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "try {\r", + " const response = await pm.sendRequest({\r", + " url: \"https://localhost:8080/api/games?pageNumber=1&pageSize=50\",\r", + " method: \"GET\"\r", + " });\r", + "\r", + " console.log(response.json());\r", + "\r", + " if(response.json().totalRecords == 0)\r", + " {\r", + " pm.variables.set(\"randomGames\", JSON.stringify([]));\r", + " return;\r", + " }\r", + "\r", + " const categories = response.json().data;\r", + " const ids = categories.map(c => c.id);\r", + "\r", + " const arrLength = Math.floor(Math.random() * 5);\r", + " const shuffledList = ids.sort(() => 0.5 - Math.random());\r", + " const randomIds = shuffledList.slice(0, arrLength);\r", + "\r", + " pm.variables.set(\"randomGames\", JSON.stringify(randomIds));\r", + "\r", + "} catch (err) {\r", + " console.error(err);\r", + "}\r", + "\r", + "const randomCardType = Math.floor(Math.random() * 3);\r", + "const randomLastFour = Math.floor(1000 + Math.random() * 9000);\r", + "\r", + "pm.variables.set(\"randomCardType\", JSON.stringify(randomCardType));\r", + "pm.variables.set(\"randomLastFourCC\", JSON.stringify(randomLastFour));" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"purchasedGameIds\": {{randomGames}},\r\n \"creditCardType\": {{randomCardType}},\r\n \"lastFourDigitsOfPaymentCard\": {{randomLastFourCC}},\r\n \"subTotal\": {{$randomPrice}},\r\n \"salesTax\": {{$randomPrice}},\r\n \"total\": {{$randomPrice}}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/sales", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales" + ] + } + }, + "response": [] + }, + { + "name": "Refund Transaction Amount From Sale", + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{baseURL}}/api/sales/:saleId?refundAmount=3", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales", + ":saleId" + ], + "query": [ + { + "key": "refundAmount", + "value": "3" + } + ], + "variable": [ + { + "key": "saleId", + "value": "5" + } + ] + } + }, + "response": [] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseURL", + "value": "https://localhost:8080" + }, + { + "key": "randomCategories", + "value": "" + }, + { + "key": "randomGames", + "value": "" + }, + { + "key": "randomLastFourCC", + "value": "", + "type": "string" + }, + { + "key": "randomCardType", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Filters.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Filters.cs new file mode 100644 index 00000000..b221a4d9 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Filters.cs @@ -0,0 +1,18 @@ +namespace EcommerceAPIDemo.Controllers; + +public class GameProductFilterParams +{ + public int? CategoryId { get; set; } + public double? MinPrice { get; set; } + public double? MaxPrice { get; set; } +} + +public class SalesFilterParams +{ + public int? GameProductId { get; set; } + public int? LastFourOfPaymentCard { get; set; } + public DateOnly? TransactionDate { get; set; } + public double? MinTransactionValue { get; set; } + public double? MaxTransactionValue { get; set; } +} + diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/GamesController.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/GamesController.cs new file mode 100644 index 00000000..0a61810d --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/GamesController.cs @@ -0,0 +1,140 @@ +using EcommerceAPIDemo.Data.DTOs; +using EcommerceAPIDemo.Data.Models; +using EcommerceAPIDemo.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace EcommerceAPIDemo.Controllers; + +[ApiController] +[Route("api/games")] +public class GamesController :ControllerBase +{ + private readonly IGamesService _gamesService; + public GamesController(IGamesService gamesService) + { + _gamesService = gamesService; + } + + [HttpGet] + public async Task>> GetAllGamesAsync([FromQuery] PaginationParams pagination, [FromQuery] GameProductFilterParams filters) + { + var query = _gamesService.GetAllGames(); + + if (query == null) + { + return NoContent(); + } + + if (filters.CategoryId.HasValue) + { + query = query.Where(p => p.Categories.Any(c => c.Id == filters.CategoryId.Value)); + } + if(filters.MinPrice.HasValue) + { + query = query.Where(p => p.Price >= filters.MinPrice.Value); + } + if (filters.MaxPrice.HasValue) + { + query = query.Where(p => p.Price < filters.MaxPrice.Value); + } + + + var totalRecords = await query.CountAsync(); + var items = await query.Skip((pagination.PageNumber - 1) * pagination.PageSize) + .Take(pagination.PageSize) + .ToListAsync(); + + var responseItems = items.Select(x => _gamesService.ConvertGameProductObjToResponseDto(x)).ToList(); + + var pagedResponse = new PagedResponse(responseItems, pagination.PageNumber, pagination.PageSize, totalRecords); + + return Ok(pagedResponse); + } + + [HttpGet("categories")] + public async Task>> GetAllCategoriesAsync([FromQuery] PaginationParams paginationParams) + { + var query = _gamesService.GetAllCategories(); + + if (query == null) + { + return NoContent(); + } + + var totalRecords = await query.CountAsync(); + var items = await query.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Take(paginationParams.PageSize) + .ToListAsync(); + + var pagedResponse = new PagedResponse(items, paginationParams.PageNumber, paginationParams.PageSize, totalRecords); + + return Ok(pagedResponse); + } + + [HttpGet("{gameId}")] + public async Task> GetGameAsync(int gameId) + { + var selectedGame = await _gamesService.GetGameAsync(gameId); + + if(selectedGame == null) + { + return NotFound($"Game with id {gameId} was not found."); + } + + return Ok(_gamesService.ConvertGameProductObjToResponseDto(selectedGame)); + } + + [HttpGet("categories/{categoryId}")] + public async Task> GetCategoryAsync(int categoryId) + { + var selectedCategory = await _gamesService.GetCategoryAsync(categoryId); + + if (selectedCategory == null) + { + return NotFound($"Category with id {categoryId} was not found."); + } + + return Ok(selectedCategory); + } + + [HttpPost] + public async Task> CreateGameAsync(NewGameDto dto) + { + var newGame = await _gamesService.CreateGameAsync(dto); + return Ok(_gamesService.ConvertGameProductObjToResponseDto(newGame)); + } + + [HttpPost("categories")] + public async Task> CreateCategoryAsync(CategoryDto dto) + { + return Ok(await _gamesService.CreateCategoryAsync(dto)); + } + + [HttpPut("{gameId}")] + public async Task> UpdateGameAsync(int gameId, ExistingGameDto dto) + { + var selectedGame = await _gamesService.GetGameAsync(gameId); + + if(selectedGame == null) + { + return NotFound($"Game with id {gameId} was not found."); + } + + var updatedGame = await _gamesService.UpdateGameAsync(selectedGame.Id, dto); + return Ok(_gamesService.ConvertGameProductObjToResponseDto(updatedGame)); + } + + [HttpPut("categories/{categoryId}")] + public async Task> UpdateCategoryAsync(int categoryId, CategoryDto dto) + { + var selectedCategory = await _gamesService.GetCategoryAsync(categoryId); + + if (selectedCategory == null) + { + return NotFound($"Category with id {categoryId} was not found."); + } + + return Ok(await _gamesService.UpdateCategoryAsync(selectedCategory.Id, dto)); + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Pagination.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Pagination.cs new file mode 100644 index 00000000..972e95a1 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/Pagination.cs @@ -0,0 +1,31 @@ +namespace EcommerceAPIDemo.Controllers; + +public class PaginationParams +{ + private const int MaxPageSize = 50; + public int PageNumber { get; set; } = 1; + + private int _pageSize = 10; + public int PageSize + { + get => _pageSize; + set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value; + } +} + +public class PagedResponse +{ + public List Data { get; set; } + public int PageNumber { get; set; } + public int PageSize { get; set; } + public int TotalRecords { get; set; } + + public PagedResponse(List data, int pageNumber, int pageSize, int totalRecords) + { + Data = data; + PageNumber = pageNumber; + PageSize = pageSize; + TotalRecords = totalRecords; + } +} + diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/SalesController.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/SalesController.cs new file mode 100644 index 00000000..22496328 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Controllers/SalesController.cs @@ -0,0 +1,114 @@ +using EcommerceAPIDemo.Data.DTOs; +using EcommerceAPIDemo.Data.Models; +using EcommerceAPIDemo.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace EcommerceAPIDemo.Controllers; + +[ApiController] +[Route("api/sales")] +public class SalesController : ControllerBase +{ + private readonly ISalesService _salesService; + public SalesController(ISalesService salesService) + { + _salesService = salesService; + } + + [HttpGet] + public async Task>> GetAllSalesAsync([FromQuery] PaginationParams pagination, [FromQuery] SalesFilterParams filters) + { + var query = _salesService.GetAllSales(); + + if (query == null) + { + return NoContent(); + } + + if(filters.GameProductId.HasValue) + { + query = query.Where(s => s.GamesPurchased.Any(p => p.Id == filters.GameProductId)); + } + if(filters.TransactionDate.HasValue) + { + query = query.Where(s => DateOnly.FromDateTime(s.TransactionDate).Equals(filters.TransactionDate)); + } + if(filters.MinTransactionValue.HasValue) + { + query = query.Where(s => s.ActualTransactionValue >= filters.MinTransactionValue); + } + if (filters.MaxTransactionValue.HasValue) + { + query = query.Where(s => s.ActualTransactionValue < filters.MaxTransactionValue); + } + + var totalRecords = await query.CountAsync(); + var items = await query.Skip((pagination.PageNumber - 1) * pagination.PageSize) + .Take(pagination.PageSize) + .ToListAsync(); + + var responseItems = items.Select(x => _salesService.ConvertSaleObjToResponseDto(x)).ToList(); + + var pagedResponse = new PagedResponse(responseItems, pagination.PageNumber, pagination.PageSize, totalRecords); + + return Ok(pagedResponse); + } + + [HttpGet("{saleId}")] + public async Task> GetSaleAsync(int saleId) + { + var selectedSale = await _salesService.GetSale(saleId); + + if (selectedSale == null) + { + return NotFound($"Sale with id {saleId} was not found."); + } + + var response = _salesService.ConvertSaleObjToResponseDto(selectedSale); + return Ok(response); + } + + [HttpPost] + public async Task> CreateSaleAsync(SaleDto dto) + { + var newSale = await _salesService.CreateSale(dto); + return Ok(_salesService.ConvertSaleObjToResponseDto(newSale)); + } + + [HttpPut("{saleId}")] + public async Task> ProcessSaleRefundAsync(int saleId, [FromQuery]double refundAmount) + { + var selectedSale = await _salesService.GetSale(saleId); + + //Check for valid input parameters + if (selectedSale == null) + { + return NotFound($"Sale with id {saleId} was not found."); + } + if(selectedSale.IsRefund) + { + return BadRequest($"Sale with id {saleId} was already refunded. No amount in transaction left to refund."); + } + if(refundAmount <= 0) + { + return BadRequest($"Amount to refund cannot be a negative value or equal to zero."); + } + if(refundAmount > selectedSale.ActualTransactionValue) + { + return BadRequest($"Sale with id {saleId} has transaction value that is less than the refund amount of {refundAmount}. Cannot refund more than what was sold."); + } + + //Full or Partial Refund? + if(refundAmount == selectedSale.ActualTransactionValue) + { + await _salesService.RefundExistingSale(saleId); + } + else + { + await _salesService.RefundPartialExistingSale(saleId, refundAmount); + } + + return Ok(_salesService.ConvertSaleObjToResponseDto(selectedSale)); + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/CategoryDto.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/CategoryDto.cs new file mode 100644 index 00000000..9d4ba480 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/CategoryDto.cs @@ -0,0 +1,6 @@ +namespace EcommerceAPIDemo.Data.DTOs; + +public class CategoryDto +{ + public string Name { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductDtos.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductDtos.cs new file mode 100644 index 00000000..db5f3aeb --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductDtos.cs @@ -0,0 +1,26 @@ +namespace EcommerceAPIDemo.Data.DTOs; + +public class NewGameDto +{ + public string Title { get; set; } + public string Description { get; set; } + public List? GameCategoryIds { get; set; } + public string Developer { get; set; } + public string Publisher { get; set; } + public DateTime ReleaseDate { get; set; } + public double Price { get; set; } + public double FileSize { get; set; } + public string SystemRequirements { get; set; } +} + +public class ExistingGameDto +{ + public string Title { get; set; } + public string Description { get; set; } + public List? GameCategoryIds { get; set; } + public string Developer { get; set; } + public string Publisher { get; set; } + public DateTime ReleaseDate { get; set; } + public double FileSize { get; set; } + public string SystemRequirements { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductResponseDto.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductResponseDto.cs new file mode 100644 index 00000000..16aef12f --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/GameProductResponseDto.cs @@ -0,0 +1,16 @@ +namespace EcommerceAPIDemo.Data.DTOs; + +public class GameProductResponseDto +{ + public int Id { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public List CategoryIds { get; } = []; + public List SalesIds { get; } = []; + public string Developer { get; set; } + public string Publisher { get; set; } + public DateTime ReleaseDate { get; set; } + public double Price { get; set; } + public double FileSize { get; set; } + public string SystemRequirements { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs new file mode 100644 index 00000000..a9770f92 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs @@ -0,0 +1,13 @@ +using EcommerceAPIDemo.Data.Models; + +namespace EcommerceAPIDemo.Data.DTOs; + +public class SaleDto +{ + public List? PurchasedGameIds { get; set; } + public CreditCardTypes creditCardType { get; set; } + public int LastFourDigitsOfPaymentCard { get; set; } + public double SubTotal { get; set; } + public double SalesTax { get; set; } + public double Total { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleResponseDto.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleResponseDto.cs new file mode 100644 index 00000000..6e210e9f --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleResponseDto.cs @@ -0,0 +1,19 @@ +using EcommerceAPIDemo.Data.Models; + +namespace EcommerceAPIDemo.Data.DTOs; + +public class SaleResponseDto +{ + public int Id { get; set; } + public DateTime TransactionDate { get; set; } + public DateTime TransactionLastUpdatedDate { get; set; } + public bool IsRefund { get; set; } + public bool IsPartialRefund { get; set; } + public List GamesPurchasedIds { get; } = []; + public CreditCardTypes CreditCardType { get; set; } + public int LastFourDigitsOfPaymentCard { get; set; } + public double SubTotal { get; set; } + public double SalesTax { get; set; } + public double Total { get; set; } + public double ActualTransactionValue { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameCategory.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameCategory.cs new file mode 100644 index 00000000..5ebe04a8 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameCategory.cs @@ -0,0 +1,7 @@ +namespace EcommerceAPIDemo.Data.Models; + +public class GameCategory +{ + public int Id { get; set; } + public string Name { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameProduct.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameProduct.cs new file mode 100644 index 00000000..4bf89dea --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/GameProduct.cs @@ -0,0 +1,16 @@ +namespace EcommerceAPIDemo.Data.Models; + +public class GameProduct +{ + public int Id { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public List Categories { get; } = []; + public List Sales { get; } = []; + public string Developer { get; set; } + public string Publisher { get; set; } + public DateTime ReleaseDate { get; set; } + public double Price { get; set; } + public double FileSize { get; set; } + public string SystemRequirements { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/Sale.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/Sale.cs new file mode 100644 index 00000000..786e46be --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/Models/Sale.cs @@ -0,0 +1,25 @@ +namespace EcommerceAPIDemo.Data.Models; + +public class Sale +{ + public int Id { get; set; } + public DateTime TransactionDate { get; set; } + public DateTime TransactionLastUpdatedDate { get; set; } + public bool IsRefund { get; set; } + public bool IsPartialRefund { get; set; } + public List GamesPurchased { get; } = []; + public CreditCardTypes CreditCardType { get; set; } + public int LastFourDigitsOfPaymentCard { get; set; } + public double SubTotal { get; set; } + public double SalesTax { get; set; } + public double Total { get; set; } + public double ActualTransactionValue { get; set; } +} + +public enum CreditCardTypes +{ + AmericanExpress, + Visa, + MasterCard, + Discover +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/SalesDbContext.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/SalesDbContext.cs new file mode 100644 index 00000000..0f9c0f82 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/SalesDbContext.cs @@ -0,0 +1,11 @@ +using EcommerceAPIDemo.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace EcommerceAPIDemo.Data; + +public class SalesDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet GameProducts { get; set; } + public DbSet GameCategories { get; set; } + public DbSet Sales { get; set; } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.csproj b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.csproj new file mode 100644 index 00000000..81bc4f68 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.http b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.http new file mode 100644 index 00000000..884eff5e --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.http @@ -0,0 +1,6 @@ +@EcommerceAPIDemo_HostAddress = http://localhost:5058 + +GET {{EcommerceAPIDemo_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.sln b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.sln new file mode 100644 index 00000000..e15a45f2 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/EcommerceAPIDemo.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36401.2 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EcommerceAPIDemo", "EcommerceAPIDemo.csproj", "{F73CBE94-1EB9-4D51-97FC-AFB34D5A6C0E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F73CBE94-1EB9-4D51-97FC-AFB34D5A6C0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F73CBE94-1EB9-4D51-97FC-AFB34D5A6C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F73CBE94-1EB9-4D51-97FC-AFB34D5A6C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F73CBE94-1EB9-4D51-97FC-AFB34D5A6C0E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {693BA877-E846-4DB7-B7BA-171D9B478CB2} + EndGlobalSection +EndGlobal diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.Designer.cs new file mode 100644 index 00000000..21cc5160 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.Designer.cs @@ -0,0 +1,177 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250810212441_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserRating") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.cs new file mode 100644 index 00000000..21283dd0 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212441_InitialCreate.cs @@ -0,0 +1,142 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "GameCategories", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameCategories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GameProducts", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Title = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: false), + Developer = table.Column(type: "nvarchar(max)", nullable: false), + Publisher = table.Column(type: "nvarchar(max)", nullable: false), + ReleaseDate = table.Column(type: "datetime2", nullable: false), + Price = table.Column(type: "float", nullable: false), + FileSize = table.Column(type: "float", nullable: false), + SystemRequirements = table.Column(type: "nvarchar(max)", nullable: false), + UserRating = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameProducts", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sales", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + TransactionDate = table.Column(type: "datetime2", nullable: false), + SubTotal = table.Column(type: "float", nullable: false), + SalesTax = table.Column(type: "float", nullable: false), + Total = table.Column(type: "float", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Sales", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GameCategoryGameProduct", + columns: table => new + { + CategoriesId = table.Column(type: "int", nullable: false), + GamesInCategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameCategoryGameProduct", x => new { x.CategoriesId, x.GamesInCategoryId }); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameCategories_CategoriesId", + column: x => x.CategoriesId, + principalTable: "GameCategories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameProducts_GamesInCategoryId", + column: x => x.GamesInCategoryId, + principalTable: "GameProducts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "GameProductSale", + columns: table => new + { + GamesPurchasedId = table.Column(type: "int", nullable: false), + SalesId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameProductSale", x => new { x.GamesPurchasedId, x.SalesId }); + table.ForeignKey( + name: "FK_GameProductSale_GameProducts_GamesPurchasedId", + column: x => x.GamesPurchasedId, + principalTable: "GameProducts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameProductSale_Sales_SalesId", + column: x => x.SalesId, + principalTable: "Sales", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GameCategoryGameProduct_GamesInCategoryId", + table: "GameCategoryGameProduct", + column: "GamesInCategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_GameProductSale_SalesId", + table: "GameProductSale", + column: "SalesId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GameCategoryGameProduct"); + + migrationBuilder.DropTable( + name: "GameProductSale"); + + migrationBuilder.DropTable( + name: "GameCategories"); + + migrationBuilder.DropTable( + name: "GameProducts"); + + migrationBuilder.DropTable( + name: "Sales"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.Designer.cs new file mode 100644 index 00000000..ce62d176 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.Designer.cs @@ -0,0 +1,177 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250810212540_UpdateGameProduct")] + partial class UpdateGameProduct + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserRating") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.cs new file mode 100644 index 00000000..1be7a832 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810212540_UpdateGameProduct.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class UpdateGameProduct : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "UserRating", + table: "GameProducts", + type: "float", + nullable: false, + oldClrType: typeof(decimal), + oldType: "decimal(18,2)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "UserRating", + table: "GameProducts", + type: "decimal(18,2)", + nullable: false, + oldClrType: typeof(double), + oldType: "float"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.Designer.cs new file mode 100644 index 00000000..63845858 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.Designer.cs @@ -0,0 +1,189 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250810220249_AddedMoreSpecificFields")] + partial class AddedMoreSpecificFields + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserRating") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.cs new file mode 100644 index 00000000..da164bcc --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810220249_AddedMoreSpecificFields.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class AddedMoreSpecificFields : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsRefund", + table: "Sales", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LastFourDigitsOfPaymentCard", + table: "Sales", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "TransactionLastUpdatedDate", + table: "Sales", + type: "datetime2", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "creditCardType", + table: "Sales", + type: "int", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsRefund", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "LastFourDigitsOfPaymentCard", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "TransactionLastUpdatedDate", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "creditCardType", + table: "Sales"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.Designer.cs new file mode 100644 index 00000000..e3cfda71 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.Designer.cs @@ -0,0 +1,186 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250810224235_EditGameProduct")] + partial class EditGameProduct + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.cs new file mode 100644 index 00000000..970d6e37 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250810224235_EditGameProduct.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class EditGameProduct : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UserRating", + table: "GameProducts"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UserRating", + table: "GameProducts", + type: "float", + nullable: false, + defaultValue: 0.0); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.Designer.cs new file mode 100644 index 00000000..4f65ec95 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.Designer.cs @@ -0,0 +1,192 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250819210036_MinorSalesModelUpdate")] + partial class MinorSalesModelUpdate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActualTransactionValue") + .HasColumnType("float"); + + b.Property("IsPartialRefund") + .HasColumnType("bit"); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.cs new file mode 100644 index 00000000..b0d8cea0 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250819210036_MinorSalesModelUpdate.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class MinorSalesModelUpdate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ActualTransactionValue", + table: "Sales", + type: "float", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "IsPartialRefund", + table: "Sales", + type: "bit", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ActualTransactionValue", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "IsPartialRefund", + table: "Sales"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.Designer.cs new file mode 100644 index 00000000..9d609a0b --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.Designer.cs @@ -0,0 +1,179 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250823224045_CategoryChange")] + partial class CategoryChange + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("GameProductId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("GameProductId"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActualTransactionValue") + .HasColumnType("float"); + + b.Property("IsPartialRefund") + .HasColumnType("bit"); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany("Categories") + .HasForeignKey("GameProductId"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Navigation("Categories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.cs new file mode 100644 index 00000000..35ee5691 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823224045_CategoryChange.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class CategoryChange : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GameCategoryGameProduct"); + + migrationBuilder.AddColumn( + name: "GameProductId", + table: "GameCategories", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories", + column: "GameProductId"); + + migrationBuilder.AddForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories", + column: "GameProductId", + principalTable: "GameProducts", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropColumn( + name: "GameProductId", + table: "GameCategories"); + + migrationBuilder.CreateTable( + name: "GameCategoryGameProduct", + columns: table => new + { + CategoriesId = table.Column(type: "int", nullable: false), + GamesInCategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameCategoryGameProduct", x => new { x.CategoriesId, x.GamesInCategoryId }); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameCategories_CategoriesId", + column: x => x.CategoriesId, + principalTable: "GameCategories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameProducts_GamesInCategoryId", + column: x => x.GamesInCategoryId, + principalTable: "GameProducts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GameCategoryGameProduct_GamesInCategoryId", + table: "GameCategoryGameProduct", + column: "GamesInCategoryId"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.Designer.cs new file mode 100644 index 00000000..a2abadda --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.Designer.cs @@ -0,0 +1,192 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250823225121_DBTestingMtoMRelationship")] + partial class DBTestingMtoMRelationship + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActualTransactionValue") + .HasColumnType("float"); + + b.Property("IsPartialRefund") + .HasColumnType("bit"); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.Property("CategoriesId") + .HasColumnType("int"); + + b.Property("GamesInCategoryId") + .HasColumnType("int"); + + b.HasKey("CategoriesId", "GamesInCategoryId"); + + b.HasIndex("GamesInCategoryId"); + + b.ToTable("GameCategoryGameProduct"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("GameCategoryGameProduct", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesInCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.cs new file mode 100644 index 00000000..cd14c484 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823225121_DBTestingMtoMRelationship.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class DBTestingMtoMRelationship : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropColumn( + name: "GameProductId", + table: "GameCategories"); + + migrationBuilder.CreateTable( + name: "GameCategoryGameProduct", + columns: table => new + { + CategoriesId = table.Column(type: "int", nullable: false), + GamesInCategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameCategoryGameProduct", x => new { x.CategoriesId, x.GamesInCategoryId }); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameCategories_CategoriesId", + column: x => x.CategoriesId, + principalTable: "GameCategories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameProducts_GamesInCategoryId", + column: x => x.GamesInCategoryId, + principalTable: "GameProducts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GameCategoryGameProduct_GamesInCategoryId", + table: "GameCategoryGameProduct", + column: "GamesInCategoryId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GameCategoryGameProduct"); + + migrationBuilder.AddColumn( + name: "GameProductId", + table: "GameCategories", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories", + column: "GameProductId"); + + migrationBuilder.AddForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories", + column: "GameProductId", + principalTable: "GameProducts", + principalColumn: "Id"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.Designer.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.Designer.cs new file mode 100644 index 00000000..569c32d5 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.Designer.cs @@ -0,0 +1,179 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + [Migration("20250823230627_GameCategoriesUpdate")] + partial class GameCategoriesUpdate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("GameProductId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("GameProductId"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActualTransactionValue") + .HasColumnType("float"); + + b.Property("IsPartialRefund") + .HasColumnType("bit"); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany("Categories") + .HasForeignKey("GameProductId"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Navigation("Categories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.cs new file mode 100644 index 00000000..c78cbfcb --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/20250823230627_GameCategoriesUpdate.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + /// + public partial class GameCategoriesUpdate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GameCategoryGameProduct"); + + migrationBuilder.AddColumn( + name: "GameProductId", + table: "GameCategories", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories", + column: "GameProductId"); + + migrationBuilder.AddForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories", + column: "GameProductId", + principalTable: "GameProducts", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_GameCategories_GameProducts_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropIndex( + name: "IX_GameCategories_GameProductId", + table: "GameCategories"); + + migrationBuilder.DropColumn( + name: "GameProductId", + table: "GameCategories"); + + migrationBuilder.CreateTable( + name: "GameCategoryGameProduct", + columns: table => new + { + CategoriesId = table.Column(type: "int", nullable: false), + GamesInCategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameCategoryGameProduct", x => new { x.CategoriesId, x.GamesInCategoryId }); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameCategories_CategoriesId", + column: x => x.CategoriesId, + principalTable: "GameCategories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameCategoryGameProduct_GameProducts_GamesInCategoryId", + column: x => x.GamesInCategoryId, + principalTable: "GameProducts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GameCategoryGameProduct_GamesInCategoryId", + table: "GameCategoryGameProduct", + column: "GamesInCategoryId"); + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/SalesDbContextModelSnapshot.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/SalesDbContextModelSnapshot.cs new file mode 100644 index 00000000..9693468e --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Migrations/SalesDbContextModelSnapshot.cs @@ -0,0 +1,176 @@ +// +using System; +using EcommerceAPIDemo.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EcommerceAPIDemo.Migrations +{ + [DbContext(typeof(SalesDbContext))] + partial class SalesDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("GameProductId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("GameProductId"); + + b.ToTable("GameCategories"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Developer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("float"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Publisher") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("SystemRequirements") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GameProducts"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.Sale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActualTransactionValue") + .HasColumnType("float"); + + b.Property("IsPartialRefund") + .HasColumnType("bit"); + + b.Property("IsRefund") + .HasColumnType("bit"); + + b.Property("LastFourDigitsOfPaymentCard") + .HasColumnType("int"); + + b.Property("SalesTax") + .HasColumnType("float"); + + b.Property("SubTotal") + .HasColumnType("float"); + + b.Property("Total") + .HasColumnType("float"); + + b.Property("TransactionDate") + .HasColumnType("datetime2"); + + b.Property("TransactionLastUpdatedDate") + .HasColumnType("datetime2"); + + b.Property("creditCardType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Sales"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.Property("GamesPurchasedId") + .HasColumnType("int"); + + b.Property("SalesId") + .HasColumnType("int"); + + b.HasKey("GamesPurchasedId", "SalesId"); + + b.HasIndex("SalesId"); + + b.ToTable("GameProductSale"); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameCategory", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany("Categories") + .HasForeignKey("GameProductId"); + }); + + modelBuilder.Entity("GameProductSale", b => + { + b.HasOne("EcommerceAPIDemo.Data.GameProduct", null) + .WithMany() + .HasForeignKey("GamesPurchasedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EcommerceAPIDemo.Data.Sale", null) + .WithMany() + .HasForeignKey("SalesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EcommerceAPIDemo.Data.GameProduct", b => + { + b.Navigation("Categories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Program.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Program.cs new file mode 100644 index 00000000..1e6fcf97 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Program.cs @@ -0,0 +1,26 @@ +using EcommerceAPIDemo.Data; +using EcommerceAPIDemo.Services; +using Microsoft.EntityFrameworkCore; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles); +builder.Services.AddDbContext(opt => opt.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); +app.MapControllers(); + +app.Run(); diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Properties/launchSettings.json b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Properties/launchSettings.json new file mode 100644 index 00000000..c068531e --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32613", + "sslPort": 44301 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5058", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7188;http://localhost:5058", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/GamesService.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/GamesService.cs new file mode 100644 index 00000000..94c6fa9a --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/GamesService.cs @@ -0,0 +1,238 @@ +using EcommerceAPIDemo.Data; +using EcommerceAPIDemo.Data.DTOs; +using EcommerceAPIDemo.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace EcommerceAPIDemo.Services; + +public interface IGamesService +{ + //READ Operations + public IQueryable? GetAllGames(); + public IQueryable? GetAllCategories(); + public Task GetGameAsync(int gameProductId); + public Task GetCategoryAsync(int categoryId); + + //CREATE Operations + public Task CreateGameAsync(NewGameDto dto); + public Task CreateCategoryAsync(CategoryDto dto); + + //UPDATE Operations + public Task UpdateGameAsync(int gameProductId, ExistingGameDto dto); + public Task UpdateCategoryAsync(int gameCategoryId, CategoryDto dto); + + //Utilities + public GameProductResponseDto ConvertGameProductObjToResponseDto(GameProduct gameProduct); +} + +public class GamesService : IGamesService +{ + private readonly SalesDbContext _salesDbContext; + + public GamesService(SalesDbContext salesDbContext) + { + _salesDbContext = salesDbContext; + } + + public async Task CreateCategoryAsync(CategoryDto dto) + { + GameCategory newCategory = ConvertDtoToGameCategory(dto); + + var savedCategory = _salesDbContext.GameCategories.Add(newCategory); + await _salesDbContext.SaveChangesAsync(); + + return savedCategory.Entity; + } + + public async Task CreateGameAsync(NewGameDto dto) + { + GameProduct newGame = await ConvertDtoToGameProductAsync(dto); + + var savedGame = _salesDbContext.GameProducts.Add(newGame); + await _salesDbContext.SaveChangesAsync(); + + return savedGame.Entity; + } + + public IQueryable? GetAllCategories() + { + var categories = _salesDbContext.GameCategories + .OrderBy(c => c.Id); + + if(!categories.Any()) + { + return null; + } + + return categories; + } + + public IQueryable? GetAllGames() + { + var games = _salesDbContext.GameProducts + .Include(p => p.Categories) + .Include(p => p.Sales) + .OrderBy(p => p.Id); + + if (!games.Any()) + { + return null; + } + + return games; + } + + public async Task GetGameAsync(int gameProductId) + { + return await _salesDbContext.GameProducts + .Include(p => p.Categories) + .Include(p => p.Sales) + .FirstOrDefaultAsync(p => p.Id == gameProductId); + } + + public async Task GetCategoryAsync(int categoryId) + { + return await _salesDbContext.GameCategories.FirstOrDefaultAsync(c => c.Id == categoryId); + } + + public async Task UpdateCategoryAsync(int gameCategoryId, CategoryDto dto) + { + var existingCategory = await _salesDbContext.GameCategories + .FirstOrDefaultAsync(c => c.Id == gameCategoryId); + + + GameCategory newCategory = ConvertDtoToGameCategory(dto); + newCategory.Id = existingCategory.Id; + + _salesDbContext.GameCategories.Entry(existingCategory).CurrentValues.SetValues(newCategory); + await _salesDbContext.SaveChangesAsync(); + + return existingCategory; + } + + public async Task UpdateGameAsync(int gameProductId, ExistingGameDto dto) + { + var existingGame = await _salesDbContext.GameProducts + .Include(p => p.Categories) + .Include(p => p.Sales) + .FirstOrDefaultAsync(p => p.Id == gameProductId); + + GameProduct newGame = await ConvertDtoToGameProductAsync(dto); + newGame.Id = existingGame.Id; + newGame.Price = existingGame.Price; + + _salesDbContext.GameProducts.Entry(existingGame).CurrentValues.SetValues(newGame); + existingGame.Categories.Clear(); + existingGame.Categories.AddRange(newGame.Categories); + await _salesDbContext.SaveChangesAsync(); + + return existingGame; + } + + public GameProductResponseDto ConvertGameProductObjToResponseDto(GameProduct gameProduct) + { + var response = new GameProductResponseDto() + { + Id = gameProduct.Id, + Title = gameProduct.Title, + Description = gameProduct.Description, + Developer = gameProduct.Developer, + Publisher = gameProduct.Publisher, + ReleaseDate = gameProduct.ReleaseDate, + Price = gameProduct.Price, + FileSize = gameProduct.FileSize, + SystemRequirements = gameProduct.SystemRequirements + }; + + if(gameProduct.Categories.Any()) + { + foreach(var category in gameProduct.Categories) + { + response.CategoryIds.Add(category.Id); + } + } + + if(gameProduct.Sales.Any()) + { + foreach(var sales in gameProduct.Sales) + { + response.SalesIds.Add(sales.Id); + } + } + + return response; + } + + private GameCategory ConvertDtoToGameCategory(CategoryDto dto) + { + GameCategory gameCategory = new() + { + Name = dto.Name + }; + + return gameCategory; + } + + private async Task ConvertDtoToGameProductAsync(NewGameDto dto) + { + GameProduct gameProduct = new() + { + Title = dto.Title, + Description = dto.Description, + Developer = dto.Developer, + Publisher = dto.Publisher, + ReleaseDate = dto.ReleaseDate, + Price = dto.Price, + FileSize = dto.FileSize, + SystemRequirements = dto.SystemRequirements + }; + + if (dto.GameCategoryIds != null) + { + foreach (var id in dto.GameCategoryIds) + { + var selectedCategory = await _salesDbContext.GameCategories + .FirstOrDefaultAsync(c => c.Id == id); + + if (selectedCategory != null) + { + _salesDbContext.Attach(selectedCategory); + gameProduct.Categories.Add(selectedCategory); + } + } + } + + return gameProduct; + } + + private async Task ConvertDtoToGameProductAsync(ExistingGameDto dto) + { + GameProduct gameProduct = new() + { + Title = dto.Title, + Description = dto.Description, + Developer = dto.Developer, + Publisher = dto.Publisher, + ReleaseDate = dto.ReleaseDate, + FileSize = dto.FileSize, + SystemRequirements = dto.SystemRequirements + }; + + if (dto.GameCategoryIds != null) + { + foreach (var id in dto.GameCategoryIds) + { + var selectedCategory = await _salesDbContext.GameCategories + .FirstOrDefaultAsync(c => c.Id == id); + + if (selectedCategory != null) + { + _salesDbContext.Attach(selectedCategory); + gameProduct.Categories.Add(selectedCategory); + } + } + } + + return gameProduct; + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs new file mode 100644 index 00000000..90b695e8 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs @@ -0,0 +1,174 @@ +using EcommerceAPIDemo.Data; +using EcommerceAPIDemo.Data.DTOs; +using EcommerceAPIDemo.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace EcommerceAPIDemo.Services; + +public interface ISalesService +{ + //READ Operations + public IQueryable? GetAllSales(); + public Task GetSale(int saleId); + + //CREATE Operations + public Task CreateSale(SaleDto dto); + + //UPDATE Operations + public Task RefundExistingSale(int saleId); + public Task RefundPartialExistingSale(int saleId, double refundAmount); + + //Utility + public SaleResponseDto ConvertSaleObjToResponseDto(Sale sale); +} + +public class SalesService : ISalesService +{ + private readonly SalesDbContext _salesDbContext; + + public SalesService(SalesDbContext salesDbContext) + { + _salesDbContext = salesDbContext; + } + + public async Task CreateSale(SaleDto dto) + { + Sale newSale = await ConvertDtoToSale(dto); + + UpdateSaleTransactionTimestamps(newSale); + newSale.IsRefund = false; + newSale.IsPartialRefund = false; + newSale.ActualTransactionValue = newSale.Total; + + var savedSale = _salesDbContext.Sales.Add(newSale); + await _salesDbContext.SaveChangesAsync(); + + return savedSale.Entity; + } + + public IQueryable? GetAllSales() + { + var sales = _salesDbContext.Sales + .Include(s => s.GamesPurchased) + .OrderBy(s => s.Id); + + if(!sales.Any()) + { + return null; + } + + return sales; + } + + public async Task GetSale(int saleId) + { + return await _salesDbContext.Sales + .Include(s => s.GamesPurchased) + .FirstOrDefaultAsync(s => s.Id == saleId); + } + + public async Task RefundExistingSale(int saleId) + { + Sale sale = await _salesDbContext.Sales + .Include(s => s.GamesPurchased) + .FirstAsync(s => s.Id == saleId); + + //This is where we'd trigger some response from a payment processor + sale.ActualTransactionValue = 0.0; + sale.IsRefund = true; + sale.IsPartialRefund = false; + UpdateSaleTransactionTimestamps(sale); + + var updatedSale = _salesDbContext.Sales.Update(sale); + await _salesDbContext.SaveChangesAsync(); + + return updatedSale.Entity; + } + + public async Task RefundPartialExistingSale(int saleId, double refundAmount) + { + Sale sale = await _salesDbContext.Sales + .Include(s => s.GamesPurchased) + .FirstAsync(s => s.Id == saleId); + + //This is where we'd trigger some response from a payment processor + sale.ActualTransactionValue -= refundAmount; + sale.IsPartialRefund = true; + UpdateSaleTransactionTimestamps(sale); + + var updatedSale = _salesDbContext.Sales.Update(sale); + await _salesDbContext.SaveChangesAsync(); + + return updatedSale.Entity; + } + + public SaleResponseDto ConvertSaleObjToResponseDto(Sale sale) + { + var responseDto = new SaleResponseDto() + { + Id = sale.Id, + TransactionDate = sale.TransactionDate, + TransactionLastUpdatedDate = sale.TransactionLastUpdatedDate, + IsRefund = sale.IsRefund, + IsPartialRefund = sale.IsPartialRefund, + CreditCardType = sale.CreditCardType, + LastFourDigitsOfPaymentCard = sale.LastFourDigitsOfPaymentCard, + SubTotal = sale.SubTotal, + SalesTax = sale.SalesTax, + Total = sale.Total, + ActualTransactionValue = sale.ActualTransactionValue + }; + + if(sale.GamesPurchased.Any()) + { + foreach(var game in sale.GamesPurchased) + { + responseDto.GamesPurchasedIds.Add(game.Id); + } + } + + return responseDto; + } + + private void UpdateSaleTransactionTimestamps(Sale sale) + { + if(sale.TransactionDate == default) + { + sale.TransactionDate = DateTime.Now; + sale.TransactionLastUpdatedDate = DateTime.Now; + } + else + { + sale.TransactionLastUpdatedDate = DateTime.Now; + } + } + + private async Task ConvertDtoToSale(SaleDto dto) + { + Sale newSale = new() + { + CreditCardType = dto.creditCardType, + LastFourDigitsOfPaymentCard = dto.LastFourDigitsOfPaymentCard, + SubTotal = dto.SubTotal, + SalesTax = dto.SalesTax, + Total = dto.Total + }; + + if (dto.PurchasedGameIds != null) + { + foreach (var id in dto.PurchasedGameIds) + { + var selectedGame = await _salesDbContext.GameProducts + .FirstOrDefaultAsync(p => p.Id == id); + + if (selectedGame != null) + { + _salesDbContext.Attach(selectedGame); + newSale.GamesPurchased.Add(selectedGame); + } + } + } + + return newSale; + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.Development.json b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.json b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.json new file mode 100644 index 00000000..fa0066ac --- /dev/null +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/appsettings.json @@ -0,0 +1,19 @@ +{ + "Kestrel": { + "Endpoints": { + "ShiftsLoggerEndpoints": { + "Url": "https://localhost:8080" + } + } + }, + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=Ecommerce;Integrated Security=true;TrustServerCertificate=True" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From 511095530099af6726834f0a838fed62b282d3ea Mon Sep 17 00:00:00 2001 From: "Ethan E." <94944955+GoldRino456@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:01:06 -0500 Subject: [PATCH 2/4] Create README.md --- EcommerceAPI.GoldRino456/README.md | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 EcommerceAPI.GoldRino456/README.md diff --git a/EcommerceAPI.GoldRino456/README.md b/EcommerceAPI.GoldRino456/README.md new file mode 100644 index 00000000..28dd74c9 --- /dev/null +++ b/EcommerceAPI.GoldRino456/README.md @@ -0,0 +1,33 @@ +# Ecommerce API Demo + +A locally hosted API simulating the back-end of an online games and digital software distributor. This project was created with the goal of better understanding Rest API design and applying it to a real world business use case. +## Features + +- Interfaces asynchronously with a SQL Server to create, store, and retrieve game product data, category information, and transaction details. +- Product and sale endpoints contain optional query parameters to filter through database results and use pagination to return a limited number of results per request. +- Game product and sales services are provided to API controllers by Dependency Injection. + + + +## Tech Stack + +**Runtime & Framework:** .NET 8, ASP.NET Core + +**Database ORM:** Entity Framework + +**Database:** SQL Server + +**API Platform**: Postman + + +## Lessons Learned + +- I found this project to be a delightful challenge and a big step up from my last exposure to building out APIs. I still have a lot to learn no doubt, but this project marks the first time I'd say I feel confident about how things work under the hood of an API. I had to deal with timing issues due to not awaiting the right methods, data formatting issues with some of my data transfer objects, and a rather annoying issue with json files and infinitely looping references to data. If I were to take another shot at this one from scratch, I think if I spent more time planning my endpoints out and how I need to structure data that moves in and outat the beginning, I'd be able to complete it much faster. + +- Postman was a bit confusing for me at first, mostly just because there appears to be so much to it from the jump, but I can really see the value of that tool now that I've reached the end of the project with it. Perhaps the most helpful bit, at least for me, was that I could create randomized entries using data from Postman. It took some tweaking and a bit of custom javascript to make it work with the "many to many" relationships my data has, but the end result was near magical. I was able to test my endpoints super easily this way and quickly populate my tables with random valid values, even random values from other existing objects in my database. +## Acknowledgements + + - [The C# Academy](https://www.thecsharpacademy.com/) - I feel like I'm learning more and more daily because of y'all! Thank you for being such a great and supportive community! + - [README Editor](https://readme.so/editor) + - [Folgers Coffee](https://www.folgerscoffee.com) + From 2c7c1c561bbb67da713b6af9ef7e62a3cf4d7d3b Mon Sep 17 00:00:00 2001 From: "Ethan E." <94944955+GoldRino456@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:01:30 -0500 Subject: [PATCH 3/4] Create README.md --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..28dd74c9 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Ecommerce API Demo + +A locally hosted API simulating the back-end of an online games and digital software distributor. This project was created with the goal of better understanding Rest API design and applying it to a real world business use case. +## Features + +- Interfaces asynchronously with a SQL Server to create, store, and retrieve game product data, category information, and transaction details. +- Product and sale endpoints contain optional query parameters to filter through database results and use pagination to return a limited number of results per request. +- Game product and sales services are provided to API controllers by Dependency Injection. + + + +## Tech Stack + +**Runtime & Framework:** .NET 8, ASP.NET Core + +**Database ORM:** Entity Framework + +**Database:** SQL Server + +**API Platform**: Postman + + +## Lessons Learned + +- I found this project to be a delightful challenge and a big step up from my last exposure to building out APIs. I still have a lot to learn no doubt, but this project marks the first time I'd say I feel confident about how things work under the hood of an API. I had to deal with timing issues due to not awaiting the right methods, data formatting issues with some of my data transfer objects, and a rather annoying issue with json files and infinitely looping references to data. If I were to take another shot at this one from scratch, I think if I spent more time planning my endpoints out and how I need to structure data that moves in and outat the beginning, I'd be able to complete it much faster. + +- Postman was a bit confusing for me at first, mostly just because there appears to be so much to it from the jump, but I can really see the value of that tool now that I've reached the end of the project with it. Perhaps the most helpful bit, at least for me, was that I could create randomized entries using data from Postman. It took some tweaking and a bit of custom javascript to make it work with the "many to many" relationships my data has, but the end result was near magical. I was able to test my endpoints super easily this way and quickly populate my tables with random valid values, even random values from other existing objects in my database. +## Acknowledgements + + - [The C# Academy](https://www.thecsharpacademy.com/) - I feel like I'm learning more and more daily because of y'all! Thank you for being such a great and supportive community! + - [README Editor](https://readme.so/editor) + - [Folgers Coffee](https://www.folgerscoffee.com) + From 93d9844361fea3b1a94a9de6424e21972000f64c Mon Sep 17 00:00:00 2001 From: "Ethan E." Date: Mon, 25 Aug 2025 23:06:59 -0500 Subject: [PATCH 4/4] Style Corrections + Remove Dupe README.md --- .../EcommerceAPIDemo/Data/DTOs/SaleDto.cs | 2 +- .../EcommerceAPIDemo/Services/SalesService.cs | 2 +- README.md | 33 ------------------- 3 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 README.md diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs index a9770f92..10072318 100644 --- a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Data/DTOs/SaleDto.cs @@ -5,7 +5,7 @@ namespace EcommerceAPIDemo.Data.DTOs; public class SaleDto { public List? PurchasedGameIds { get; set; } - public CreditCardTypes creditCardType { get; set; } + public CreditCardTypes CreditCardType { get; set; } public int LastFourDigitsOfPaymentCard { get; set; } public double SubTotal { get; set; } public double SalesTax { get; set; } diff --git a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs index 90b695e8..56db0c63 100644 --- a/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs +++ b/EcommerceAPI.GoldRino456/EcommerceAPIDemo/Services/SalesService.cs @@ -147,7 +147,7 @@ private async Task ConvertDtoToSale(SaleDto dto) { Sale newSale = new() { - CreditCardType = dto.creditCardType, + CreditCardType = dto.CreditCardType, LastFourDigitsOfPaymentCard = dto.LastFourDigitsOfPaymentCard, SubTotal = dto.SubTotal, SalesTax = dto.SalesTax, diff --git a/README.md b/README.md deleted file mode 100644 index 28dd74c9..00000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Ecommerce API Demo - -A locally hosted API simulating the back-end of an online games and digital software distributor. This project was created with the goal of better understanding Rest API design and applying it to a real world business use case. -## Features - -- Interfaces asynchronously with a SQL Server to create, store, and retrieve game product data, category information, and transaction details. -- Product and sale endpoints contain optional query parameters to filter through database results and use pagination to return a limited number of results per request. -- Game product and sales services are provided to API controllers by Dependency Injection. - - - -## Tech Stack - -**Runtime & Framework:** .NET 8, ASP.NET Core - -**Database ORM:** Entity Framework - -**Database:** SQL Server - -**API Platform**: Postman - - -## Lessons Learned - -- I found this project to be a delightful challenge and a big step up from my last exposure to building out APIs. I still have a lot to learn no doubt, but this project marks the first time I'd say I feel confident about how things work under the hood of an API. I had to deal with timing issues due to not awaiting the right methods, data formatting issues with some of my data transfer objects, and a rather annoying issue with json files and infinitely looping references to data. If I were to take another shot at this one from scratch, I think if I spent more time planning my endpoints out and how I need to structure data that moves in and outat the beginning, I'd be able to complete it much faster. - -- Postman was a bit confusing for me at first, mostly just because there appears to be so much to it from the jump, but I can really see the value of that tool now that I've reached the end of the project with it. Perhaps the most helpful bit, at least for me, was that I could create randomized entries using data from Postman. It took some tweaking and a bit of custom javascript to make it work with the "many to many" relationships my data has, but the end result was near magical. I was able to test my endpoints super easily this way and quickly populate my tables with random valid values, even random values from other existing objects in my database. -## Acknowledgements - - - [The C# Academy](https://www.thecsharpacademy.com/) - I feel like I'm learning more and more daily because of y'all! Thank you for being such a great and supportive community! - - [README Editor](https://readme.so/editor) - - [Folgers Coffee](https://www.folgerscoffee.com) -