Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

217 new admin endpoints: search user #233

Merged
merged 5 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace CoffeeCard.Library.Migrations
{
public partial class NewIndexName : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Name",
schema: "dbo",
table: "Users",
type: "nvarchar(450)",
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(max)");

migrationBuilder.CreateIndex(
name: "IX_Users_Name",
schema: "dbo",
table: "Users",
column: "Name");
}

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Users_Name",
schema: "dbo",
table: "Users");

migrationBuilder.AlterColumn<string>(
name: "Name",
schema: "dbo",
table: "Users",
type: "nvarchar(max)",
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(450)");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <auto-generated />
// <auto-generated />
using System;
using CoffeeCard.Library.Persistence;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -304,7 +304,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)

b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
.HasColumnType("nvarchar(450)");

b.Property<string>("Password")
.IsRequired()
Expand Down Expand Up @@ -332,6 +332,8 @@ protected override void BuildModel(ModelBuilder modelBuilder)

b.HasIndex("Email");

b.HasIndex("Name");

b.HasIndex("ProgrammeId");

b.ToTable("Users", "dbo");
Expand Down
42 changes: 41 additions & 1 deletion coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using CoffeeCard.Models.DataTransferObjects.v2.User;
using CoffeeCard.Models.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Serilog;

Expand Down Expand Up @@ -210,6 +209,47 @@ public async Task UpdateUserGroup(UserGroup userGroup, int userId)
await _context.SaveChangesAsync();
}


public async Task<UserSearchResponse> SearchUsers(String search, int pageNum, int pageLength)
{
int skip = pageNum * pageLength;

IQueryable<User> query;
if (string.IsNullOrEmpty(search))
{
query = _context.Users;
}
else
{
query = _context.Users
.Where(u => EF.Functions.Like(u.Id.ToString(), $"%{search}%") ||
EF.Functions.Like(u.Name, $"%{search}%") ||
EF.Functions.Like(u.Email, $"%{search}%"));
}

var totalUsers = await query.CountAsync();

var userByPage = await query
.OrderBy(u => u.Id)
.Skip(skip).Take(pageLength)
.Select(u => new SimpleUserResponse
{
Id = u.Id,
Name = u.Name,
Email = u.Email,
UserGroup = u.UserGroup,
State = u.UserState
})
.ToListAsync();

return new UserSearchResponse
{
TotalUsers = totalUsers,
Users = userByPage
};
}


private async Task<User> GetUserByIdAsync(int id)
{
var user = await _context.Users
Expand Down
9 changes: 9 additions & 0 deletions coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
Expand Down Expand Up @@ -63,5 +64,13 @@ public interface IAccountService
/// <param name="userGroup"> The user group that will be updated </param>
/// <param name="id"> id of the user </param>
Task UpdateUserGroup(UserGroup userGroup, int id);

/// <summary>
/// Search a user from the database
/// </summary>
/// <param name="search"> The search string from a search bar </param>
/// <param name="pageNum"> The page number </param>
/// <param name="pageLength"> The length of a page </param>
Task<UserSearchResponse> SearchUsers(String search, int pageNum, int pageLength);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.User
/// "name": "John Doe",
/// "email": "[email protected]",
/// "password": "[no example provided]",
/// "programme": 1
/// "programmeId": 1
/// }
/// </example>
public class RegisterAccountRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using CoffeeCard.Models.Entities;

namespace CoffeeCard.Models.DataTransferObjects.v2.User
{
public class SimpleUserResponse
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public UserGroup UserGroup { get; set; }
public UserState State { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace CoffeeCard.Models.DataTransferObjects.v2.User;

/// <summary>
/// Represents a search result
/// </summary>
/// <example>
/// {
/// "users": [
/// {
/// "id": 12232,
/// "name": "John Doe",
/// "email": "[email protected]",
/// "userGroup": "Barista",
/// "state": "Active"
/// }
/// ],
/// "totalUsers": 1
/// }
/// </example>
public class UserSearchResponse
{
/// <summary>
/// The number of users that match the query
/// </summary>
/// <value> Users number </value>
/// <example>1</example>
[Required]
public int TotalUsers { get; set; }

/// <summary>
/// The users that match the query
/// </summary>
/// <value> Users List </value>
/// <example>
/// [
/// {
/// "id": 12232,
/// "name": "John Doe",
/// "email": "[email protected]",
/// "userGroup": "Barista",
/// "state": "Active"
/// }
/// ],
/// </example>
[Required]
public IEnumerable<SimpleUserResponse> Users;
}
1 change: 1 addition & 0 deletions coffeecard/CoffeeCard.Models/Entities/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace CoffeeCard.Models.Entities
{
[Index(nameof(Email))]
[Index(nameof(Name))]
public class User
{
public int Id { get; set; }
Expand Down
23 changes: 23 additions & 0 deletions coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CoffeeCard.Common.Errors;
using CoffeeCard.Library.Utils;
Expand All @@ -10,6 +13,7 @@
using CoffeeCard.Library.Services.v2;
using CoffeeCard.Models.Entities;
using CoffeeCard.WebApi.Helpers;
using System.ComponentModel.DataAnnotations;

namespace CoffeeCard.WebApi.Controllers.v2
{
Expand Down Expand Up @@ -201,5 +205,24 @@ private async Task<UserResponse> UserWithRanking(User user)
PrivacyActivated = user.PrivacyActivated,
};
}

/// <summary>
/// Searches a user in the database
/// </summary>
/// <param name="filter">A filter to search by Id, Name or Email. When an empty string is given, all users will be returned</param>
/// <param name="pageNum">The page number</param>
/// <param name="pageLength">The length of a page</param>
/// <returns> A collection of User objects that match the search criteria </returns>
/// <response code="200">Users, possible with filter applied</response>
/// <response code="401"> Invalid credentials </response>
[HttpGet]
[AuthorizeRoles(UserGroup.Board)]
[ProducesResponseType(typeof(ApiError), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(SimpleUserResponse), StatusCodes.Status200OK)]
[Route("search")]
public async Task<ActionResult<IEnumerable<SimpleUserResponse>>> SearchUsers([FromQuery][Range(0, int.MaxValue)] int pageNum, [FromQuery] string filter = "", [FromQuery][Range(1, 100)] int pageLength = 30)
{
return Ok(await _accountService.SearchUsers(filter, pageNum, pageLength));
}
}
}
2 changes: 1 addition & 1 deletion coffeecard/CoffeeCard.WebApi/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"DeploymentUrl": "https://localhost:8080/"
},
"DatabaseSettings": {
"ConnectionString": "Server=localhost;Initial Catalog=master;User=sa;Password=Your_password123;TrustServerCertificate=True;",
"ConnectionString": "Server=mssql;Initial Catalog=master;User=sa;Password=Your_password123;TrustServerCertificate=True;",
"SchemaName": "dbo"
},
"IdentitySettings": {
Expand Down
Loading