diff --git a/.github/workflows/master_userapp124.yml b/.github/workflows/master_userapp124.yml new file mode 100644 index 00000000..90adccd7 --- /dev/null +++ b/.github/workflows/master_userapp124.yml @@ -0,0 +1,66 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy ASP app to Azure Web App - userApp124 + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup MSBuild path + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v1.0.5 + + - name: Restore NuGet packages + run: nuget restore + + - name: Publish to folder + run: msbuild /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="\published\" + + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v3 + with: + name: ASP-app + path: '/published/**' + + deploy: + runs-on: windows-latest + needs: build + environment: + name: 'Production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + permissions: + id-token: write #This is required for requesting the JWT + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v3 + with: + name: ASP-app + + - name: Login to Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_D6D4FAAD52464D5C85E05B4FBBC20E28 }} + tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_4EB6EDC292D346DBA08192CD5B3693FB }} + subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_1A12925687414AFE8A34F49FC22F0108 }} + + - name: Deploy to Azure Web App + id: deploy-to-webapp + uses: azure/webapps-deploy@v2 + with: + app-name: 'userApp124' + slot-name: 'Production' + package: . + \ No newline at end of file diff --git a/CRUD application 2.csproj b/CRUD application 2.csproj index 553546be..7adf77b4 100644 --- a/CRUD application 2.csproj +++ b/CRUD application 2.csproj @@ -1,6 +1,5 @@  - Debug @@ -45,6 +44,9 @@ 4 + + packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.4.1.0\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + @@ -110,10 +112,14 @@ True packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll - - - - packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + packages\xunit.assert.2.8.1\lib\netstandard1.1\xunit.assert.dll + + + packages\xunit.extensibility.core.2.8.1\lib\net452\xunit.core.dll @@ -121,6 +127,7 @@ + Global.asax @@ -138,6 +145,9 @@ + + + @@ -210,11 +220,12 @@ + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - + \ No newline at end of file diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs index f9ecbda2..beb02eda 100644 --- a/Controllers/UserController.cs +++ b/Controllers/UserController.cs @@ -7,60 +7,124 @@ namespace CRUD_application_2.Controllers public class UserController : Controller { public static System.Collections.Generic.List userlist = new System.Collections.Generic.List(); - // GET: User + + /// + /// Retrieves the list of users and displays them in the Index view. + /// + /// The Index view with the list of users. public ActionResult Index() { - // Implement the Index method here + return View(userlist); } - - // GET: User/Details/5 + + /// + /// Retrieves the details of a specific user based on the provided ID and displays them in the Details view. + /// + /// The ID of the user to retrieve. + /// The Details view with the details of the user. public ActionResult Details(int id) { - // Implement the details method here + return View(userlist.FirstOrDefault(x => x.Id == id)); } - - // GET: User/Create + + /// + /// Displays the view to create a new user. + /// + /// The Create view. public ActionResult Create() { - //Implement the Create method here + return View(); } - - // POST: User/Create + + /// + /// Handles the HTTP POST request to create a new user. + /// + /// The user object containing the information of the new user. + /// Redirects to the Index action to display the updated list of users. [HttpPost] public ActionResult Create(User user) { - // Implement the Create method (POST) here + userlist.Add(user); + return RedirectToAction("Index"); } - - // GET: User/Edit/5 + + /// + /// Displays the view to edit an existing user with the specified ID. + /// + /// The ID of the user to edit. + /// The Edit view with the user to edit. public ActionResult Edit(int id) { - // This method is responsible for displaying the view to edit an existing user with the specified ID. - // It retrieves the user from the userlist based on the provided ID and passes it to the Edit view. + User user = userlist.FirstOrDefault(x => x.Id == id); + if (user == null) + { + return HttpNotFound(); + } + return View(user); } - - // POST: User/Edit/5 + + /// + /// Handles the HTTP POST request to update an existing user with the specified ID. + /// + /// The ID of the user to update. + /// The user object containing the updated information. + /// Redirects to the Index action to display the updated list of users. [HttpPost] public ActionResult Edit(int id, User user) { - // This method is responsible for handling the HTTP POST request to update an existing user with the specified ID. - // It receives user input from the form submission and updates the corresponding user's information in the userlist. - // If successful, it redirects to the Index action to display the updated list of users. - // If no user is found with the provided ID, it returns a HttpNotFoundResult. - // If an error occurs during the process, it returns the Edit view to display any validation errors. + User existingUser = userlist.FirstOrDefault(x => x.Id == id); + if (existingUser == null) + { + return HttpNotFound(); + } + existingUser.Name = user.Name; + existingUser.Email = user.Email; + + return RedirectToAction("Index"); } - - // GET: User/Delete/5 + + /// + /// Displays the view to delete an existing user with the specified ID. + /// + /// The ID of the user to delete. + /// The Delete view with the user to delete. public ActionResult Delete(int id) { - // Implement the Delete method here + User user = userlist.FirstOrDefault(x => x.Id == id); + if (user == null) + { + return HttpNotFound(); + } + return View(user); } - - // POST: User/Delete/5 + + /// + /// Handles the HTTP POST request to delete an existing user with the specified ID. + /// + /// The ID of the user to delete. + /// The form collection. + /// Redirects to the Index action to display the updated list of users. [HttpPost] public ActionResult Delete(int id, FormCollection collection) { - // Implement the Delete method (POST) here + User user = userlist.FirstOrDefault(x => x.Id == id); + if (user == null) + { + return HttpNotFound(); + } + userlist.Remove(user); + return RedirectToAction("Index"); + } + + /// + /// Retrieves the list of users that match the provided search term and displays them in the Search view. + /// + /// The search term to match against user names. + /// The Search view with the list of matching users. + public ActionResult Search(string searchTerm) + { + var searchResults = userlist.Where(user => user.Name.Contains(searchTerm)).ToList(); + return View(searchResults); } } } diff --git a/Controllers/UserControllerTests.cs b/Controllers/UserControllerTests.cs new file mode 100644 index 00000000..0aa49aea --- /dev/null +++ b/Controllers/UserControllerTests.cs @@ -0,0 +1,172 @@ +using CRUD_application_2.Controllers; +using CRUD_application_2.Models; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Xunit; + +namespace CRUD_application_2.Tests.Controllers +{ + public class UserControllerTests + { + [Fact] + public void Index_ReturnsViewWithUserList() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + + // Act + var result = controller.Index() as ViewResult; + + // Assert + Assert.NotNull(result); + Assert.Equal(userList, result.Model); + } + + [Fact] + public void Details_ReturnsViewWithUser() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + var userId = 1; + + // Act + var result = controller.Details(userId) as ViewResult; + + // Assert + Assert.NotNull(result); + Assert.Equal(userList.FirstOrDefault(x => x.Id == userId), result.Model); + } + + [Fact] + public void Create_ReturnsView() + { + // Arrange + var controller = new UserController(); + + // Act + var result = controller.Create() as ViewResult; + + // Assert + Assert.NotNull(result); + } + + [Fact] + public void Create_Post_AddsUserToListAndRedirectsToIndex() + { + // Arrange + var controller = new UserController(); + var userList = new List(); + UserController.userlist = userList; + var user = new User { Id = 1, Name = "John", Email = "john@example.com" }; + + // Act + var result = controller.Create(user) as RedirectToRouteResult; + + // Assert + Assert.NotNull(result); + Assert.Equal("Index", result.RouteValues["action"]); + Assert.Contains(user, userList); + } + + [Fact] + public void Edit_ReturnsViewWithUser() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + var userId = 1; + + // Act + var result = controller.Edit(userId) as ViewResult; + + // Assert + Assert.NotNull(result); + Assert.Equal(userList.FirstOrDefault(x => x.Id == userId), result.Model); + } + + [Fact] + public void Edit_Post_UpdatesUserAndRedirectsToIndex() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + var userId = 1; + var user = new User { Id = 1, Name = "Updated John", Email = "updatedjohn@example.com" }; + + // Act + var result = controller.Edit(userId, user) as RedirectToRouteResult; + + // Assert + Assert.NotNull(result); + Assert.Equal("Index", result.RouteValues["action"]); + Assert.Equal(user.Name, userList.FirstOrDefault(x => x.Id == userId)?.Name); + Assert.Equal(user.Email, userList.FirstOrDefault(x => x.Id == userId)?.Email); + } + + [Fact] + public void Delete_ReturnsViewWithUser() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + var userId = 1; + + // Act + var result = controller.Delete(userId) as ViewResult; + + // Assert + Assert.NotNull(result); + Assert.Equal(userList.FirstOrDefault(x => x.Id == userId), result.Model); + } + + [Fact] + public void Delete_Post_RemovesUserFromListAndRedirectsToIndex() + { + // Arrange + var controller = new UserController(); + var userList = new List + { + new User { Id = 1, Name = "John", Email = "john@example.com" }, + new User { Id = 2, Name = "Jane", Email = "jane@example.com" } + }; + UserController.userlist = userList; + var userId = 1; + + // Act + var result = controller.Delete(userId, null) as RedirectToRouteResult; + + // Assert + Assert.NotNull(result); + Assert.Equal("Index", result.RouteValues["action"]); + Assert.DoesNotContain(userList.FirstOrDefault(x => x.Id == userId), userList); + } + } +} diff --git a/README.MD b/README.MD new file mode 100644 index 00000000..f82e14c7 --- /dev/null +++ b/README.MD @@ -0,0 +1,75 @@ +# Solution Documentation + +## Introduction +This solution is designed to solve a specific problem or address a particular requirement. It provides a set of functionalities and components that work together to achieve the desired outcome. + +## Usage +To use this solution, follow the steps below: + +1. Install the necessary dependencies and libraries. +2. Configure the solution according to your specific environment or requirements. +3. Build and deploy the solution to the target system. +4. Execute the solution and provide the required inputs. +5. Monitor the solution's output and verify the results. + +## Components +The solution consists of the following components: + +### Component 1 +Description: This component is responsible for... + +Usage: To use this component, follow the steps below: + +1. Instantiate the component object. +2. Set the necessary properties or parameters. +3. Call the appropriate methods or functions. +4. Handle any returned values or exceptions. + +### Component 2 +Description: This component is responsible for... + +Usage: To use this component, follow the steps below: + +1. Instantiate the component object. +2. Set the necessary properties or parameters. +3. Call the appropriate methods or functions. +4. Handle any returned values or exceptions. + +## Configuration +The solution may require some configuration to adapt it to your specific environment or requirements. The configuration options include: + +1. Configuration Option 1: Description of the configuration option and how to set it. +2. Configuration Option 2: Description of the configuration option and how to set it. +3. ... + +## Troubleshooting +If you encounter any issues while using this solution, refer to the following troubleshooting guide: + +1. Issue 1: Description of the issue and possible causes. + - Solution: Steps to resolve the issue or workarounds. +2. Issue 2: Description of the issue and possible causes. + - Solution: Steps to resolve the issue or workarounds. +3. ... + +## Limitations +This solution may have some limitations or constraints that you should be aware of. These limitations include: + +1. Limitation 1: Description of the limitation and its impact. +2. Limitation 2: Description of the limitation and its impact. +3. ... + +## License +This solution is licensed under the [insert license name]. Please refer to the LICENSE file for more information. + +## Contributing +If you would like to contribute to this solution, please follow the guidelines outlined in the CONTRIBUTING.md file. + +## Support +If you need any assistance or have any questions, please contact [insert contact information]. + +## Acknowledgements +We would like to acknowledge the following individuals or organizations for their contributions or support in developing this solution: + +- [Name/Organization 1] +- [Name/Organization 2] +- ... diff --git a/Views/User/Index.cshtml b/Views/User/Index.cshtml index 543196b1..5dcbf953 100644 --- a/Views/User/Index.cshtml +++ b/Views/User/Index.cshtml @@ -9,6 +9,15 @@

@Html.ActionLink("Create New", "Create")

+ +@using (Html.BeginForm("Index", "User", FormMethod.Get)) +{ +

+ @Html.TextBox("searchString", ViewBag.CurrentFilter as string) + +

+} +
diff --git a/Web.config b/Web.config index d246aab1..0ff37a5a 100644 --- a/Web.config +++ b/Web.config @@ -30,7 +30,7 @@ - + @@ -48,8 +48,8 @@ - - + + \ No newline at end of file diff --git a/deploy.json b/deploy.json new file mode 100644 index 00000000..77f5db64 --- /dev/null +++ b/deploy.json @@ -0,0 +1,186 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "sites_userApp124_name": { + "defaultValue": "userApp124", + "type": "String" + }, + "serverfarms_ASP_GitHubCopilotChallenges_ad32_externalid": { + "defaultValue": "/subscriptions/8f59d1f1-6b84-417c-84f2-465227460185/resourceGroups/GitHub-Copilot-Challenges/providers/Microsoft.Web/serverfarms/ASP-GitHubCopilotChallenges-ad32", + "type": "String" + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Web/sites", + "apiVersion": "2023-12-01", + "name": "[parameters('sites_userApp124_name')]", + "location": "East US", + "kind": "app", + "properties": { + "enabled": true, + "hostNameSslStates": [ + { + "name": "userapp124.azurewebsites.net", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "userapp124.scm.azurewebsites.net", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[parameters('serverfarms_ASP_GitHubCopilotChallenges_ad32_externalid')]", + "reserved": false, + "isXenon": false, + "hyperV": false, + "dnsConfiguration": {}, + "vnetRouteAllEnabled": false, + "vnetImagePullEnabled": false, + "vnetContentShareEnabled": false, + "siteConfig": { + "numberOfWorkers": 1, + "acrUseManagedIdentityCreds": false, + "alwaysOn": false, + "http20Enabled": false, + "functionAppScaleLimit": 0, + "minimumElasticInstanceCount": 0 + }, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "clientCertMode": "Required", + "hostNamesDisabled": false, + "vnetBackupRestoreEnabled": false, + "customDomainVerificationId": "E2FDF542FE1442DC6590B5C4AFD10531861FB18A8C572DBC088708718270A5F5", + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": true, + "redundancyMode": "None", + "publicNetworkAccess": "Enabled", + "storageAccountRequired": false, + "keyVaultReferenceIdentity": "SystemAssigned" + } + }, + { + "type": "Microsoft.Web/sites/basicPublishingCredentialsPolicies", + "apiVersion": "2023-12-01", + "name": "[concat(parameters('sites_userApp124_name'), '/ftp')]", + "location": "East US", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('sites_userApp124_name'))]" + ], + "properties": { + "allow": false + } + }, + { + "type": "Microsoft.Web/sites/basicPublishingCredentialsPolicies", + "apiVersion": "2023-12-01", + "name": "[concat(parameters('sites_userApp124_name'), '/scm')]", + "location": "East US", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('sites_userApp124_name'))]" + ], + "properties": { + "allow": false + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2023-12-01", + "name": "[concat(parameters('sites_userApp124_name'), '/web')]", + "location": "East US", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('sites_userApp124_name'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "acrUseManagedIdentityCreds": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "$userApp124", + "scmType": "GitHubAction", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": false, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": false + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "vnetRouteAllEnabled": false, + "vnetPrivatePortsCount": 0, + "publicNetworkAccess": "Enabled", + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 2147483647, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 2147483647, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "scmMinTlsVersion": "1.2", + "ftpsState": "FtpsOnly", + "preWarmedInstanceCount": 0, + "elasticWebAppScaleLimit": 0, + "functionsRuntimeScaleMonitoringEnabled": false, + "minimumElasticInstanceCount": 0, + "azureStorageAccounts": {} + } + }, + { + "type": "Microsoft.Web/sites/hostNameBindings", + "apiVersion": "2023-12-01", + "name": "[concat(parameters('sites_userApp124_name'), '/', parameters('sites_userApp124_name'), '.azurewebsites.net')]", + "location": "East US", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('sites_userApp124_name'))]" + ], + "properties": { + "siteName": "userApp124", + "hostNameType": "Verified" + } + } + ] +} \ No newline at end of file diff --git a/deploy.parameters.json b/deploy.parameters.json new file mode 100644 index 00000000..684a6b60 --- /dev/null +++ b/deploy.parameters.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "sites_userApp124_name": { + "value": "userApp124" + } + } +} diff --git a/packages.config b/packages.config index 40f85c9e..3f27fba3 100644 --- a/packages.config +++ b/packages.config @@ -8,10 +8,13 @@ - + + + + \ No newline at end of file