diff --git a/application/endpoints/src/main/resources/openapi/documentation.yaml b/application/endpoints/src/main/resources/openapi/documentation.yaml index a224b16b..33368b60 100644 --- a/application/endpoints/src/main/resources/openapi/documentation.yaml +++ b/application/endpoints/src/main/resources/openapi/documentation.yaml @@ -317,17 +317,16 @@ paths: required: true schema: type: string + - name: friendId + in: query + required: true + schema: + type: string delete: summary: Delete friend tags: - friends description: "Unsubscribing from a user, as a result of which he ceases to be your friend" - requestBody: - description: Creation token based on nickname - content: - application/json: - schema: - $ref: '#/components/schemas/AccessFriendRequest' responses: "200": description: An example of a successful generated response with an token @@ -627,17 +626,16 @@ paths: required: true schema: type: string + - name: meetingId + in: query + required: true + schema: + type: string delete: summary: Deleting a meeting tags: - meetings description: "Removing a meeting from the database" - requestBody: - description: response with an token - content: - application/json: - schema: - $ref: '#/components/schemas/AccessMeetingIdRequest' responses: "200": description: An example of a successful generated response with an token @@ -1299,7 +1297,7 @@ components: example: "John" required: - "nickname" - additionalProperties: false + additionalProperties: true GenerateIdentityResponse: type: "object" properties: @@ -1312,7 +1310,7 @@ components: required: - "status" - "result" - additionalProperties: false + additionalProperties: true AccessMeetingIdRequest: type: "object" properties: @@ -1322,7 +1320,7 @@ components: required: - "token" - "meetingId" - additionalProperties: false + additionalProperties: true ListMeetingParticipantsRequest: type: "object" properties: @@ -1337,7 +1335,7 @@ components: - "token" - "meetingId" - "amount" - additionalProperties: false + additionalProperties: true ListMeetingParticipantsResponse: type: "object" properties: @@ -1358,7 +1356,7 @@ components: required: - "result" - "status" - additionalProperties: false + additionalProperties: true ListMapMeetingsRequest: type: object properties: @@ -1385,7 +1383,7 @@ components: - "14:BtEpi2o77Uia9NvwQqbumtNggJzjlEVS5gTS1Z8SDs9BmXk7E8lGxji5MBHxcor0j9X1tMgTjiYwbVvuqjdhkdDTr4XtZY6lFhURbKYG17LlfgqPVi6xJpoAdwQyxLz6wILAaiJdBy3wK1PZ3t0Zg3oBGeyeKJtfestgJylmEMCBjH7eXkBP9Bi93xkV4u68kB4OBKPesxJrTpUHlQjwuP8bCpj8Q591BUdUm9t9dXR4xSksMKWWWe4t4Y4ZrAfA" required: - "accessIdentity" - additionalProperties: false + additionalProperties: true IdentityRequest: type: object properties: @@ -1399,7 +1397,7 @@ components: - "14:BtEpi2o77Uia9NvwQqbumtNggJzjlEVS5gTS1Z8SDs9BmXk7E8lGxji5MBHxcor0j9X1tMgTjiYwbVvuqjdhkdDTr4XtZY6lFhURbKYG17LlfgqPVi6xJpoAdwQyxLz6wILAaiJdBy3wK1PZ3t0Zg3oBGeyeKJtfestgJylmEMCBjH7eXkBP9Bi93xkV4u68kB4OBKPesxJrTpUHlQjwuP8bCpj8Q591BUdUm9t9dXR4xSksMKWWWe4t4Y4ZrAfA" required: - "id" - additionalProperties: false + additionalProperties: true AccessFriendRequest: type: "object" properties: @@ -1408,7 +1406,7 @@ components: example: "5:PjvgzvEu2YsHIm5OP4L0apfqtQV9Ck9I02ZuhJxDqJ4VeS9SwQJGpBRV0QhvbaDJjsTaMZRVRzsM3yn9uSvBffdY1ul9k3J7IDgWqgbQXHlI0quTL0ORJAUONHzzRfLHs6sOmTAf7AlRP4AmGu9aqwN6hALE9MeDITcY3LlNJR6HPE5z3SK1aE5OC2YKpjhX0xQLBQd6wlkdeoCPhyJRevx60u1T1kVIhpnjBLyXQZwbHYGPCIVJ5ryeVByQCQI8" required: - "friendId" - additionalProperties: false + additionalProperties: true ListFriendsRequest: type: "object" properties: @@ -1445,7 +1443,7 @@ components: required: - "email" - "confirmHash" - additionalProperties: false + additionalProperties: true EmailLinkRequest: type: "object" properties: @@ -1455,7 +1453,7 @@ components: required: - "email" - "token" - additionalProperties: false + additionalProperties: true TelegramFinishRequest: type: "object" properties: @@ -1489,7 +1487,7 @@ components: required: - "accessIdentity" - "fileIdentityIdentity" - additionalProperties: false + additionalProperties: true CreateMeetingRequest: type: "object" properties: @@ -1521,7 +1519,7 @@ components: - "date" - "location" - "visibility" - additionalProperties: false + additionalProperties: true EditMeetingResponse: type: object properties: @@ -1566,7 +1564,7 @@ components: required: - "token" - "meetingId" - additionalProperties: false + additionalProperties: true InvalidResponse: type: object properties: @@ -1585,7 +1583,7 @@ components: - "status" - "errorCode" - "errorMessage" - additionalProperties: false + additionalProperties: true StatusTrueResponse: type: object properties: @@ -1594,7 +1592,7 @@ components: example: true required: - "status" - additionalProperties: false + additionalProperties: true TelegramPreloginResponse: type: object properties: @@ -1819,7 +1817,7 @@ components: example: "8:bqLhYy1hjNTL0wXcN0dnmSeyZDMt2akkGYMDdaa81oc8fPhOHBWTuFWzMdWNqu7gTHHrfhvC64RE2ZEtngYD5YVpOAGz8Nx1ii7l6BxpBCfHgaL6hHzkEqsmuGlea2gijOEZFkDEhebZ9lwkav13DMTdWJUm4GMm0VQgXzX8s3B528XsMxkXiu5ps9JrRB4m2m4vnZHEaskq9SXEGfhxzeuVft4d2eHWlp5V06NPmfhuyehq9J3GwWxYSkN3Doun" required: - token - additionalProperties: false + additionalProperties: true EditUserRequest: type: object properties: diff --git a/application/src/test/kotlin/TestFriends.kt b/application/src/test/kotlin/TestFriends.kt index 45dd1402..d22c8605 100644 --- a/application/src/test/kotlin/TestFriends.kt +++ b/application/src/test/kotlin/TestFriends.kt @@ -45,6 +45,20 @@ class TestFriends { assertNull(user.relationship) } + @Test + fun `test friend delete`() = runTestServer { + val self = generateTestAccount() + val friend = generateTestAccount() + + self.friends.add(friend.id) + friend.friends.add(self.id) + + require(self.friends.list(1.amount).data.first().id == friend.id) + + self.friends.delete(friend.id) + require(self.friends.list(1.amount).data.isEmpty()) + } + @Test fun `test relationship property`() = runTestServer { val self = generateTestAccount() diff --git a/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/FriendId.kt b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/FriendId.kt new file mode 100644 index 00000000..cf4ab12a --- /dev/null +++ b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/FriendId.kt @@ -0,0 +1,16 @@ +package app.meetacy.backend.core.endpoints + +import app.meetacy.backend.types.serializable.serialization +import app.meetacy.backend.types.serializable.users.UserId +import io.ktor.http.* +import kotlinx.serialization.SerializationException + + +fun Parameters.friendIdOrNull(name: String = "friendId"): UserId? { + return this[name]?.let(::UserId) +} + +fun Parameters.friendId(name: String = "friendId"): UserId = serialization { + val friendId = this[name] ?: throw SerializationException("Bad request. Illegal input: param 'friendId' is required for type with serial name, but it was missing at path: $name") + UserId(friendId) +} diff --git a/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/MeetingId.kt b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/MeetingId.kt new file mode 100644 index 00000000..5c783715 --- /dev/null +++ b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/MeetingId.kt @@ -0,0 +1,15 @@ +package app.meetacy.backend.core.endpoints + +import app.meetacy.backend.types.serializable.meetings.MeetingId +import app.meetacy.backend.types.serializable.serialization +import io.ktor.http.* +import kotlinx.serialization.SerializationException + +fun Parameters.meetingIdOrNull(name: String = "meetingId"): MeetingId? { + return this[name]?.let(::MeetingId) +} + +fun Parameters.meetingId(name: String = "meetingId"): MeetingId = serialization { + val meetingId = this[name] ?: throw SerializationException("Bad request. Illegal input: param 'meetingId' is required for type with serial name, but it was missing at path: $name") + MeetingId(meetingId) +} diff --git a/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/UserId.kt b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/UserId.kt index 23bc2b53..d8f27d6b 100644 --- a/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/UserId.kt +++ b/core/endpoints/src/main/kotlin/app/meetacy/backend/core/endpoints/UserId.kt @@ -1,9 +1,15 @@ package app.meetacy.backend.core.endpoints +import app.meetacy.backend.types.serializable.serialization import app.meetacy.backend.types.serializable.users.UserId import io.ktor.http.* -import io.ktor.server.application.* +import kotlinx.serialization.SerializationException fun Parameters.userIdOrNull(name: String = "userId"): UserId? { return this[name]?.let(::UserId) } + +fun Parameters.userId(name: String = "userId"): UserId = serialization { + val userId = this[name] ?: throw SerializationException("Bad request. Illegal input: param 'userId' is required for type with serial name, but it was missing at path: $name") + UserId(userId) +} diff --git a/feature/friends/endpoints/src/main/kotlin/app/meetacy/backend/feature/friends/endpoints/delete/Routing.kt b/feature/friends/endpoints/src/main/kotlin/app/meetacy/backend/feature/friends/endpoints/delete/Routing.kt index bc9b65bc..1f890b81 100644 --- a/feature/friends/endpoints/src/main/kotlin/app/meetacy/backend/feature/friends/endpoints/delete/Routing.kt +++ b/feature/friends/endpoints/src/main/kotlin/app/meetacy/backend/feature/friends/endpoints/delete/Routing.kt @@ -1,25 +1,19 @@ package app.meetacy.backend.feature.friends.endpoints.delete import app.meetacy.backend.core.endpoints.accessIdentity +import app.meetacy.backend.core.endpoints.friendId import app.meetacy.backend.endpoint.ktor.Failure import app.meetacy.backend.endpoint.ktor.respondFailure import app.meetacy.backend.endpoint.ktor.respondSuccess import app.meetacy.backend.types.serializable.access.AccessIdentity -import app.meetacy.backend.types.serializable.users.UserId as UserIdentitySerializable import io.ktor.server.application.* -import io.ktor.server.request.* import io.ktor.server.routing.* -import kotlinx.serialization.Serializable +import app.meetacy.backend.types.serializable.users.UserId as UserIdentitySerializable interface DeleteFriendRepository { suspend fun deleteFriend(token: AccessIdentity, friendId: UserIdentitySerializable): DeleteFriendResult } -@Serializable -data class DeleteFriendParam( - val friendId: UserIdentitySerializable -) - sealed interface DeleteFriendResult { data object Success : DeleteFriendResult data object InvalidIdentity : DeleteFriendResult @@ -27,10 +21,10 @@ sealed interface DeleteFriendResult { } fun Route.deleteFriend(provider: DeleteFriendRepository) = delete("/delete") { - val param = call.receive() + val param = call.parameters.friendId() val token = call.accessIdentity() - when (provider.deleteFriend(token, param.friendId)) { + when (provider.deleteFriend(token, param)) { DeleteFriendResult.FriendNotFound -> call.respondFailure(Failure.FriendNotFound) DeleteFriendResult.InvalidIdentity -> call.respondFailure(Failure.InvalidToken) DeleteFriendResult.Success -> call.respondSuccess() diff --git a/feature/meetings/endpoints/src/main/kotlin/app/meetacy/backend/feature/meetings/endpoints/delete/Routing.kt b/feature/meetings/endpoints/src/main/kotlin/app/meetacy/backend/feature/meetings/endpoints/delete/Routing.kt index 7ca9253e..8ed5a1ea 100644 --- a/feature/meetings/endpoints/src/main/kotlin/app/meetacy/backend/feature/meetings/endpoints/delete/Routing.kt +++ b/feature/meetings/endpoints/src/main/kotlin/app/meetacy/backend/feature/meetings/endpoints/delete/Routing.kt @@ -1,20 +1,14 @@ package app.meetacy.backend.feature.meetings.endpoints.delete import app.meetacy.backend.core.endpoints.accessIdentity +import app.meetacy.backend.core.endpoints.meetingId import app.meetacy.backend.endpoint.ktor.Failure import app.meetacy.backend.endpoint.ktor.respondFailure import app.meetacy.backend.endpoint.ktor.respondSuccess import app.meetacy.backend.types.serializable.access.AccessIdentity import app.meetacy.backend.types.serializable.meetings.MeetingId import io.ktor.server.application.* -import io.ktor.server.request.* import io.ktor.server.routing.* -import kotlinx.serialization.Serializable - -@Serializable -data class DeleteMeetingParam( - val meetingId: MeetingId -) sealed interface DeleteMeetingResult { data object Success : DeleteMeetingResult @@ -27,9 +21,9 @@ interface DeleteMeetingRepository { } fun Route.deleteMeeting(repository: DeleteMeetingRepository) = delete("/delete") { - val param = call.receive() + val param = call.parameters.meetingId() val token = call.accessIdentity() - when (repository.deleteMeeting(token, param.meetingId)) { + when (repository.deleteMeeting(token, param)) { is DeleteMeetingResult.Success -> call.respondSuccess() is DeleteMeetingResult.InvalidIdentity -> call.respondFailure(Failure.InvalidToken) is DeleteMeetingResult.MeetingNotFound -> call.respondFailure(Failure.InvalidMeetingIdentity) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37e2b46e..f213c1bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ ssh = "2.10.1" shadow = "7.0.0" wdater = "0.0.4" -meetacySdk = "0.0.71-builde09e00c" +meetacySdk = "0.0.71-build80f4055" meetacyDi = "0.0.25" [libraries]