Skip to content

Commit

Permalink
Add get user profile by id (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
mczachurski authored Oct 25, 2024
1 parent 922469f commit 5b85d07
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
27 changes: 19 additions & 8 deletions Sources/VernissageServer/Controllers/UsersController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ struct UsersController {
/// User profile.
///
/// The endpoint returns data about the user. This is a public endpoint
/// that can also be accessed by non-logged-in users.
/// that can also be accessed by non-logged-in users. You can pass here
/// user name or user id.
///
/// > Important: Endpoint URL: `/api/v1/users/:userName`.
///
Expand Down Expand Up @@ -305,21 +306,31 @@ struct UsersController {
@Sendable
func read(request: Request) async throws -> UserDto {

guard let userName = request.parameters.get("name") else {
guard let userNameOrId = request.parameters.get("name") else {
throw Abort(.badRequest)
}

let usersService = request.application.services.usersService
let userNameNormalized = userName.deletingPrefix("@").uppercased()
let userFromDb = try await usersService.get(on: request.db, userName: userNameNormalized)
var isProfileOwner = false
var userFromDb: User? = nil

if userNameOrId.starts(with: "@") || !userNameOrId.isNumber {
let userNameNormalized = userNameOrId.deletingPrefix("@").uppercased()
userFromDb = try await usersService.get(on: request.db, userName: userNameNormalized)

let userNameFromToken = request.auth.get(UserPayload.self)?.userName
isProfileOwner = userNameFromToken?.uppercased() == userNameNormalized
} else if let userId = Int64(userNameOrId) {
userFromDb = try await usersService.get(on: request.db, id: userId)

let userIdFromToken = request.auth.get(UserPayload.self)?.id
isProfileOwner = userIdFromToken == userNameOrId
}

guard let user = userFromDb else {
throw EntityNotFoundError.userNotFound
}

let userNameFromToken = request.auth.get(UserPayload.self)?.userName
let isProfileOwner = userNameFromToken?.uppercased() == userNameNormalized

let userProfile = await usersService.convertToDto(on: request,
user: user,
flexiFields: user.flexiFields,
Expand Down Expand Up @@ -1683,7 +1694,7 @@ struct UsersController {
attachSensitive: false)
return userProfile
}

private func relationship(on request: Request, sourceId: Int64, targetUser: User) async throws -> RelationshipDto {
let targetUserId = try targetUser.requireID()
let relationshipsService = request.application.services.relationshipsService
Expand Down
14 changes: 14 additions & 0 deletions Sources/VernissageServer/Extensions/String+Numeric.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// https://mczachurski.dev
// Copyright © 2024 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//

import Foundation

extension String {
var isNumber: Bool {
let digitsCharacters = CharacterSet(charactersIn: "0123456789")
return CharacterSet(charactersIn: self).isSubset(of: digitsCharacters)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ extension ControllersTests {
self.application = try await ApplicationManager.shared.application()
}

@Test("testUserProfileShouldBeReturnedForExistingUser")
func userProfileShouldBeReturnedForExistingUser() async throws {
@Test("User profile should be returned for existing user by user name")
func userProfileShouldBeReturnedForExistingUserByUserName() async throws {

// Arrange.
let user = try await application.createUser(userName: "johnbush")
Expand All @@ -42,7 +42,29 @@ extension ControllersTests {
#expect(userDto.bio == user.bio, "Property 'bio' should be equal.")
}

@Test("testUserProfileShouldNotBeReturnedForNotExistingUser")
@Test("User profile should be returned for existing user by user id")
func userProfileShouldBeReturnedForExistingUserByUserId() async throws {

// Arrange.
let user = try await application.createUser(userName: "clarabush")

// Act.
let userDto = try application.getResponse(
as: .user(userName: "clarabush", password: "p@ssword"),
to: "/users/" + (user.stringId() ?? ""),
decodeTo: UserDto.self
)

// Assert.
#expect(userDto.id == user.stringId(), "Property 'id' should be equal.")
#expect(userDto.account == user.account, "Property 'userName' should be equal.")
#expect(userDto.userName == user.userName, "Property 'userName' should be equal.")
#expect(userDto.email == user.email, "Property 'email' should be equal.")
#expect(userDto.name == user.name, "Property 'name' should be equal.")
#expect(userDto.bio == user.bio, "Property 'bio' should be equal.")
}

@Test("User profile should not be returned for not existing user")
func userProfileShouldNotBeReturnedForNotExistingUser() throws {

// Act.
Expand All @@ -52,7 +74,7 @@ extension ControllersTests {
#expect(response.status == HTTPResponseStatus.notFound, "Response http status code should be not found (404).")
}

@Test("testPublicProfileShouldNotContainsSensitiveInformation")
@Test("Public profile should not contains sensitive information")
func publicProfileShouldNotContainsSensitiveInformation() async throws {

// Arrange.
Expand Down

0 comments on commit 5b85d07

Please sign in to comment.