diff --git a/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.Designer.cs b/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.Designer.cs
new file mode 100644
index 00000000..2ba45670
--- /dev/null
+++ b/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.Designer.cs
@@ -0,0 +1,574 @@
+//
+using System;
+using CoffeeCard.Library.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace CoffeeCard.Library.Migrations
+{
+ [DbContext(typeof(CoffeeCardContext))]
+ [Migration("20240111163619_NewIndexName")]
+ partial class NewIndexName
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("dbo")
+ .HasAnnotation("ProductVersion", "6.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.LoginAttempt", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("Time")
+ .HasColumnType("datetime2");
+
+ b.Property("User_Id")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("User_Id");
+
+ b.ToTable("LoginAttempts", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.PosPurhase", b =>
+ {
+ b.Property("PurchaseId")
+ .HasColumnType("int");
+
+ b.Property("BaristaInitials")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("PurchaseId");
+
+ b.ToTable("PosPurchases", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Product", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ExperienceWorth")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("NumberOfTickets")
+ .HasColumnType("int");
+
+ b.Property("Price")
+ .HasColumnType("int");
+
+ b.Property("Visible")
+ .HasColumnType("bit");
+
+ b.HasKey("Id");
+
+ b.ToTable("Products", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.ProductUserGroup", b =>
+ {
+ b.Property("ProductId")
+ .HasColumnType("int");
+
+ b.Property("UserGroup")
+ .HasColumnType("int");
+
+ b.HasKey("ProductId", "UserGroup");
+
+ b.ToTable("ProductUserGroups", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Programme", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("FullName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ShortName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SortPriority")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.ToTable("Programmes", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Purchase", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("DateCreated")
+ .HasColumnType("datetime2");
+
+ b.Property("ExternalTransactionId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("NumberOfTickets")
+ .HasColumnType("int");
+
+ b.Property("OrderId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Price")
+ .HasColumnType("int");
+
+ b.Property("ProductId")
+ .HasColumnType("int");
+
+ b.Property("ProductName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PurchasedById")
+ .HasColumnType("int")
+ .HasColumnName("PurchasedBy_Id");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ExternalTransactionId");
+
+ b.HasIndex("OrderId")
+ .IsUnique();
+
+ b.HasIndex("ProductId");
+
+ b.HasIndex("PurchasedById");
+
+ b.ToTable("Purchases", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Statistic", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("ExpiryDate")
+ .HasColumnType("datetime2");
+
+ b.Property("LastSwipe")
+ .HasColumnType("datetime2");
+
+ b.Property("Preset")
+ .HasColumnType("int");
+
+ b.Property("SwipeCount")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int")
+ .HasColumnName("User_Id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("Preset", "ExpiryDate");
+
+ b.ToTable("Statistics", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Ticket", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("DateCreated")
+ .HasColumnType("datetime2");
+
+ b.Property("DateUsed")
+ .HasColumnType("datetime2");
+
+ b.Property("IsUsed")
+ .HasColumnType("bit");
+
+ b.Property("OwnerId")
+ .HasColumnType("int")
+ .HasColumnName("Owner_Id");
+
+ b.Property("ProductId")
+ .HasColumnType("int");
+
+ b.Property("PurchaseId")
+ .HasColumnType("int")
+ .HasColumnName("Purchase_Id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OwnerId");
+
+ b.HasIndex("PurchaseId");
+
+ b.ToTable("Tickets", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Token", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("TokenHash")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .HasColumnType("int")
+ .HasColumnName("User_Id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Tokens", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("DateCreated")
+ .HasColumnType("datetime2");
+
+ b.Property("DateUpdated")
+ .HasColumnType("datetime2");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Experience")
+ .HasColumnType("int");
+
+ b.Property("IsVerified")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PrivacyActivated")
+ .HasColumnType("bit");
+
+ b.Property("ProgrammeId")
+ .HasColumnType("int")
+ .HasColumnName("Programme_Id");
+
+ b.Property("Salt")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserGroup")
+ .HasColumnType("int");
+
+ b.Property("UserState")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Email");
+
+ b.HasIndex("Name");
+
+ b.HasIndex("ProgrammeId");
+
+ b.ToTable("Users", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Voucher", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1);
+
+ b.Property("Code")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("DateCreated")
+ .HasColumnType("datetime2");
+
+ b.Property("DateUsed")
+ .HasColumnType("datetime2");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProductId")
+ .HasColumnType("int")
+ .HasColumnName("Product_Id");
+
+ b.Property("PurchaseId")
+ .HasColumnType("int");
+
+ b.Property("Requester")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .HasColumnType("int")
+ .HasColumnName("User_Id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.HasIndex("ProductId");
+
+ b.HasIndex("PurchaseId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Vouchers", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.WebhookConfiguration", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("LastUpdated")
+ .HasColumnType("datetime2");
+
+ b.Property("SignatureKey")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("WebhookConfigurations", "dbo");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.LoginAttempt", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.User", "User")
+ .WithMany("LoginAttempts")
+ .HasForeignKey("User_Id")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.PosPurhase", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.Purchase", "Purchase")
+ .WithMany()
+ .HasForeignKey("PurchaseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Purchase");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.ProductUserGroup", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.Product", "Product")
+ .WithMany("ProductUserGroup")
+ .HasForeignKey("ProductId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Product");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Purchase", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.Product", "Product")
+ .WithMany()
+ .HasForeignKey("ProductId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("CoffeeCard.Models.Entities.User", "PurchasedBy")
+ .WithMany("Purchases")
+ .HasForeignKey("PurchasedById")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Product");
+
+ b.Navigation("PurchasedBy");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Statistic", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.User", "User")
+ .WithMany("Statistics")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Ticket", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.User", "Owner")
+ .WithMany("Tickets")
+ .HasForeignKey("OwnerId")
+ .OnDelete(DeleteBehavior.NoAction)
+ .IsRequired();
+
+ b.HasOne("CoffeeCard.Models.Entities.Purchase", "Purchase")
+ .WithMany("Tickets")
+ .HasForeignKey("PurchaseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Owner");
+
+ b.Navigation("Purchase");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Token", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.User", "User")
+ .WithMany("Tokens")
+ .HasForeignKey("UserId");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.User", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.Programme", "Programme")
+ .WithMany("Users")
+ .HasForeignKey("ProgrammeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Programme");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Voucher", b =>
+ {
+ b.HasOne("CoffeeCard.Models.Entities.Product", "Product")
+ .WithMany()
+ .HasForeignKey("ProductId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("CoffeeCard.Models.Entities.Purchase", "Purchase")
+ .WithMany()
+ .HasForeignKey("PurchaseId");
+
+ b.HasOne("CoffeeCard.Models.Entities.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId");
+
+ b.Navigation("Product");
+
+ b.Navigation("Purchase");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Product", b =>
+ {
+ b.Navigation("ProductUserGroup");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Programme", b =>
+ {
+ b.Navigation("Users");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.Purchase", b =>
+ {
+ b.Navigation("Tickets");
+ });
+
+ modelBuilder.Entity("CoffeeCard.Models.Entities.User", b =>
+ {
+ b.Navigation("LoginAttempts");
+
+ b.Navigation("Purchases");
+
+ b.Navigation("Statistics");
+
+ b.Navigation("Tickets");
+
+ b.Navigation("Tokens");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.cs b/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.cs
new file mode 100644
index 00000000..46fbab57
--- /dev/null
+++ b/coffeecard/CoffeeCard.Library/Migrations/20240111163619_NewIndexName.cs
@@ -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(
+ 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(
+ name: "Name",
+ schema: "dbo",
+ table: "Users",
+ type: "nvarchar(max)",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "nvarchar(450)");
+ }
+ }
+}
diff --git a/coffeecard/CoffeeCard.Library/Migrations/CoffeeCardContextModelSnapshot.cs b/coffeecard/CoffeeCard.Library/Migrations/CoffeeCardContextModelSnapshot.cs
index 558a6e23..863f3a34 100644
--- a/coffeecard/CoffeeCard.Library/Migrations/CoffeeCardContextModelSnapshot.cs
+++ b/coffeecard/CoffeeCard.Library/Migrations/CoffeeCardContextModelSnapshot.cs
@@ -1,4 +1,4 @@
-//
+//
using System;
using CoffeeCard.Library.Persistence;
using Microsoft.EntityFrameworkCore;
@@ -304,7 +304,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Name")
.IsRequired()
- .HasColumnType("nvarchar(max)");
+ .HasColumnType("nvarchar(450)");
b.Property("Password")
.IsRequired()
@@ -332,6 +332,8 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.HasIndex("Email");
+ b.HasIndex("Name");
+
b.HasIndex("ProgrammeId");
b.ToTable("Users", "dbo");
diff --git a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs
index 0b72d071..3a308c4d 100644
--- a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs
+++ b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs
@@ -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;
@@ -210,6 +209,47 @@ public async Task UpdateUserGroup(UserGroup userGroup, int userId)
await _context.SaveChangesAsync();
}
+
+ public async Task SearchUsers(String search, int pageNum, int pageLength)
+ {
+ int skip = pageNum * pageLength;
+
+ IQueryable 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 GetUserByIdAsync(int id)
{
var user = await _context.Users
diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs
index 9a35b4f2..42166a66 100644
--- a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs
+++ b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
@@ -63,5 +64,13 @@ public interface IAccountService
/// The user group that will be updated
/// id of the user
Task UpdateUserGroup(UserGroup userGroup, int id);
+
+ ///
+ /// Search a user from the database
+ ///
+ /// The search string from a search bar
+ /// The page number
+ /// The length of a page
+ Task SearchUsers(String search, int pageNum, int pageLength);
}
}
\ No newline at end of file
diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/RegisterAccountRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/RegisterAccountRequest.cs
index 44f2f124..b39499a4 100644
--- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/RegisterAccountRequest.cs
+++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/RegisterAccountRequest.cs
@@ -10,7 +10,7 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.User
/// "name": "John Doe",
/// "email": "john@doe.com",
/// "password": "[no example provided]",
- /// "programme": 1
+ /// "programmeId": 1
/// }
///
public class RegisterAccountRequest
diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/SimpleUserResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/SimpleUserResponse.cs
new file mode 100644
index 00000000..d033bb34
--- /dev/null
+++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/SimpleUserResponse.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UserSearchResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UserSearchResponse.cs
new file mode 100644
index 00000000..bdf65ca7
--- /dev/null
+++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UserSearchResponse.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace CoffeeCard.Models.DataTransferObjects.v2.User;
+
+///
+/// Represents a search result
+///
+///
+/// {
+/// "users": [
+/// {
+/// "id": 12232,
+/// "name": "John Doe",
+/// "email": "johndoe@itu.dk",
+/// "userGroup": "Barista",
+/// "state": "Active"
+/// }
+/// ],
+/// "totalUsers": 1
+/// }
+///
+public class UserSearchResponse
+{
+ ///
+ /// The number of users that match the query
+ ///
+ /// Users number
+ /// 1
+ [Required]
+ public int TotalUsers { get; set; }
+
+ ///
+ /// The users that match the query
+ ///
+ /// Users List
+ ///
+ /// [
+ /// {
+ /// "id": 12232,
+ /// "name": "John Doe",
+ /// "email": "johndoe@itu.dk",
+ /// "userGroup": "Barista",
+ /// "state": "Active"
+ /// }
+ /// ],
+ ///
+ [Required]
+ public IEnumerable Users;
+}
\ No newline at end of file
diff --git a/coffeecard/CoffeeCard.Models/Entities/User.cs b/coffeecard/CoffeeCard.Models/Entities/User.cs
index 3a557d20..0bdeb613 100644
--- a/coffeecard/CoffeeCard.Models/Entities/User.cs
+++ b/coffeecard/CoffeeCard.Models/Entities/User.cs
@@ -8,6 +8,7 @@
namespace CoffeeCard.Models.Entities
{
[Index(nameof(Email))]
+ [Index(nameof(Name))]
public class User
{
public int Id { get; set; }
diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs
index 3751d641..bfdefd8f 100644
--- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs
+++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs
@@ -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;
@@ -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
{
@@ -201,5 +205,24 @@ private async Task UserWithRanking(User user)
PrivacyActivated = user.PrivacyActivated,
};
}
+
+ ///
+ /// Searches a user in the database
+ ///
+ /// A filter to search by Id, Name or Email. When an empty string is given, all users will be returned
+ /// The page number
+ /// The length of a page
+ /// A collection of User objects that match the search criteria
+ /// Users, possible with filter applied
+ /// Invalid credentials
+ [HttpGet]
+ [AuthorizeRoles(UserGroup.Board)]
+ [ProducesResponseType(typeof(ApiError), StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(typeof(SimpleUserResponse), StatusCodes.Status200OK)]
+ [Route("search")]
+ public async Task>> 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));
+ }
}
}
\ No newline at end of file
diff --git a/coffeecard/CoffeeCard.WebApi/appsettings.json b/coffeecard/CoffeeCard.WebApi/appsettings.json
index d65081e1..a5aaf876 100644
--- a/coffeecard/CoffeeCard.WebApi/appsettings.json
+++ b/coffeecard/CoffeeCard.WebApi/appsettings.json
@@ -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": {