start migration onto ktor-based serialization of data
This commit is contained in:
parent
399405a4fb
commit
ff973e63fc
@ -10,6 +10,7 @@ import dev.inmo.micro_utils.ktor.server.configurators.*
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.sessions.sessions
|
||||
@ -24,84 +25,72 @@ fun User.principal() = AuthUserPrincipal(this)
|
||||
|
||||
class AuthenticationRoutingConfigurator(
|
||||
private val authFeature: AuthFeature,
|
||||
private val authTokensService: AuthTokensService,
|
||||
private val unifiedRouter: UnifiedRouter
|
||||
private val authTokensService: AuthTokensService
|
||||
) : ApplicationRoutingConfigurator.Element, ApplicationAuthenticationConfigurator.Element {
|
||||
override fun Route.invoke() {
|
||||
unifiedRouter.apply {
|
||||
route(authRootPathPart) {
|
||||
post(authAuthPathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
it.printStackTrace()
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
val creds = uniload(AuthCreds.serializer())
|
||||
|
||||
val tokenInfo = authFeature.auth(creds)
|
||||
|
||||
if (tokenInfo == null) {
|
||||
if (call.response.status() == null) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
} else {
|
||||
call.sessions.set(tokenSessionKey, tokenInfo.token)
|
||||
unianswer(
|
||||
AuthTokenInfo.serializer().nullable,
|
||||
tokenInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
post (authRefreshPathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
val refreshToken = uniload(RefreshToken.serializer())
|
||||
|
||||
val tokenInfo = authFeature.refresh(refreshToken)
|
||||
|
||||
if (tokenInfo == null) {
|
||||
if (call.response.status() == null) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
} else {
|
||||
call.sessions.set(tokenSessionKey, tokenInfo.token)
|
||||
unianswer(
|
||||
AuthTokenInfo.serializer().nullable,
|
||||
tokenInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
post(authGetMePathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
unianswer(
|
||||
User.serializer().nullable,
|
||||
authFeature.getMe(
|
||||
uniload(AuthToken.serializer())
|
||||
)
|
||||
route(authRootPathPart) {
|
||||
post(authAuthPathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
it.printStackTrace()
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
val creds = call.receive<AuthCreds>()
|
||||
|
||||
val tokenInfo = authFeature.auth(creds)
|
||||
|
||||
if (tokenInfo == null) {
|
||||
if (call.response.status() == null) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
} else {
|
||||
call.sessions.set(tokenSessionKey, tokenInfo.token)
|
||||
call.respond(tokenInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
post (authRefreshPathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
val refreshToken = call.receive<RefreshToken>()
|
||||
|
||||
val tokenInfo = authFeature.refresh(refreshToken)
|
||||
|
||||
if (tokenInfo == null) {
|
||||
if (call.response.status() == null) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
} else {
|
||||
call.sessions.set(tokenSessionKey, tokenInfo.token)
|
||||
call.respond(tokenInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
post(authGetMePathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
call.respond(
|
||||
HttpStatusCode.InternalServerError,
|
||||
"Something went wrong"
|
||||
)
|
||||
}
|
||||
) {
|
||||
call.respond(
|
||||
authFeature.getMe(call.receive()) ?: HttpStatusCode.NoContent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package dev.inmo.postssystem.features.content.common
|
||||
|
||||
import dev.inmo.micro_utils.common.FileName
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.mime_types.MimeType
|
||||
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
|
||||
import kotlinx.serialization.PolymorphicSerializer
|
||||
@ -30,6 +29,18 @@ data class BinaryContent(
|
||||
) : Content
|
||||
|
||||
val ContentSerializer = PolymorphicSerializer(Content::class)
|
||||
@Serializable
|
||||
data class ContentWrapper(
|
||||
val content: Content
|
||||
)
|
||||
@Serializable
|
||||
data class ContentsWrapper(
|
||||
val content: List<Content>
|
||||
)
|
||||
@Serializable
|
||||
data class ContentsEithersWrapper(
|
||||
val content: List<Either<ContentId, Content>>
|
||||
)
|
||||
|
||||
/**
|
||||
* Content which is already registered in database. Using its [id] you can retrieve all known
|
||||
|
@ -25,7 +25,6 @@ class ClientReadFilesStorage(
|
||||
MetaFileInfoStorageWrapper.serializer().nullable,
|
||||
FileId.serializer()
|
||||
) {
|
||||
private val unifiedRequester = UnifiedRequester(client, serialFormat)
|
||||
private val fullFilesPath = buildStandardUrl(baseUrl, filesRootPathPart)
|
||||
private val fullFilesGetBytesPath = buildStandardUrl(
|
||||
fullFilesPath,
|
||||
|
@ -4,10 +4,13 @@ import dev.inmo.postssystem.features.files.common.*
|
||||
import dev.inmo.postssystem.features.files.common.storage.*
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadStandardCrudRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureWriteStandardCrudRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.*
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.decodeURLQueryComponent
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondBytes
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
@ -15,47 +18,33 @@ import kotlinx.serialization.builtins.nullable
|
||||
class FilesRoutingConfigurator(
|
||||
private val filesStorage: ReadFilesStorage,
|
||||
private val writeFilesStorage: WriteFilesStorage?,
|
||||
private val unifierRouter: UnifiedRouter
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
constructor(filesStorage: FilesStorage, unifierRouter: UnifiedRouter) : this(filesStorage, filesStorage, unifierRouter)
|
||||
constructor(filesStorage: FilesStorage) : this(filesStorage, filesStorage)
|
||||
|
||||
override fun Route.invoke() {
|
||||
authenticate {
|
||||
route(filesRootPathPart) {
|
||||
configureReadStandardCrudRepoRoutes(
|
||||
configureReadCRUDRepoRoutes(
|
||||
filesStorage,
|
||||
MetaFileInfoStorageWrapper.serializer(),
|
||||
MetaFileInfoStorageWrapper.serializer().nullable,
|
||||
FileId.serializer(),
|
||||
unifierRouter
|
||||
::FileId
|
||||
)
|
||||
writeFilesStorage ?.let {
|
||||
configureWriteStandardCrudRepoRoutes(
|
||||
writeFilesStorage,
|
||||
FullFileInfoStorageWrapper.serializer(),
|
||||
FullFileInfoStorageWrapper.serializer().nullable,
|
||||
FullFileInfo.serializer(),
|
||||
FileId.serializer(),
|
||||
unifierRouter
|
||||
)
|
||||
configureWriteCRUDRepoRoutes(writeFilesStorage)
|
||||
}
|
||||
|
||||
unifierRouter.apply {
|
||||
post(filesGetFilesPathPart) {
|
||||
call.respondBytes(
|
||||
filesStorage.getBytes(
|
||||
uniload(FileId.serializer())
|
||||
)
|
||||
post(filesGetFilesPathPart) {
|
||||
call.respondBytes(
|
||||
filesStorage.getBytes(
|
||||
call.receive()
|
||||
)
|
||||
}
|
||||
get(filesGetFullFileInfoPathPart) {
|
||||
unianswer(
|
||||
FullFileInfoStorageWrapper.serializer().nullable,
|
||||
filesStorage.getFullFileInfo(
|
||||
decodeUrlQueryValueOrSendError(filesFileIdParameter, FileId.serializer()) ?: return@get
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
get(filesGetFullFileInfoPathPart) {
|
||||
call.respond(
|
||||
filesStorage.getFullFileInfo(
|
||||
FileId(call.getParameterOrSendError(filesFileIdParameter) ?.decodeURLQueryComponent() ?: return@get)
|
||||
) ?: HttpStatusCode.NoContent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ import kotlinx.serialization.KSerializer
|
||||
|
||||
class ClientRolesStorage<T : Role>(
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester,
|
||||
private val client: HttpClient,
|
||||
private val serializer: KSerializer<T>
|
||||
) : RolesStorage<T>,
|
||||
ReadRolesStorage<T> by ReadClientRolesStorage(
|
||||
baseUrl, unifiedRequester, serializer
|
||||
baseUrl, client, serializer
|
||||
),
|
||||
WriteRolesStorage<T> by WriteClientRolesStorage(
|
||||
baseUrl, unifiedRequester, serializer
|
||||
baseUrl, client, serializer
|
||||
)
|
||||
|
@ -3,13 +3,15 @@ package dev.inmo.postssystem.features.roles.client
|
||||
import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
class ReadClientRolesStorage<T : Role>(
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester,
|
||||
private val client: HttpClient,
|
||||
private val serializer: KSerializer<T>
|
||||
) : ReadRolesStorage<T> {
|
||||
private val userRolesSerializer = ListSerializer(serializer)
|
||||
@ -21,14 +23,13 @@ class ReadClientRolesStorage<T : Role>(
|
||||
|
||||
override suspend fun getSubjects(
|
||||
role: T
|
||||
): List<RoleSubject> = unifiedRequester.uniget(
|
||||
): List<RoleSubject> = client.get(
|
||||
buildStandardUrl(
|
||||
userRolesFullUrl,
|
||||
usersRolesGetSubjectsPathPart,
|
||||
usersRolesRoleQueryParameterName to unifiedRequester.encodeUrlQueryValue(serializer, role)
|
||||
),
|
||||
RoleSubjectsSerializer
|
||||
)
|
||||
)
|
||||
).body()
|
||||
|
||||
override suspend fun getRoles(
|
||||
subject: RoleSubject
|
||||
|
@ -8,7 +8,7 @@ import kotlinx.serialization.builtins.serializer
|
||||
|
||||
class WriteClientRolesStorage<T : Role>(
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester,
|
||||
private val client: HttpClient,
|
||||
private val serializer: KSerializer<T>
|
||||
) : WriteRolesStorage<T> {
|
||||
private val wrapperSerializer = RolesStorageIncludeExcludeWrapper.serializer(
|
||||
|
@ -6,7 +6,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
@Serializable(RoleSerializer::class)
|
||||
@Polymorphic
|
||||
interface Role {
|
||||
companion object {
|
||||
fun serializer(): KSerializer<Role> = RoleSerializer
|
||||
|
@ -1,17 +1,14 @@
|
||||
package dev.inmo.postssystem.features.roles.manager.server
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
||||
import dev.inmo.postssystem.features.roles.common.RolesStorage
|
||||
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRole
|
||||
import dev.inmo.postssystem.features.roles.server.RolesStorageWriteServerRoutesConfigurator
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
|
||||
class RolesManagerRolesStorageServerRoutesConfigurator(
|
||||
storage: RolesStorage<RolesManagerRole>,
|
||||
unifiedRouter: UnifiedRouter
|
||||
storage: RolesStorage<RolesManagerRole>
|
||||
) : ApplicationRoutingConfigurator.Element by RolesStorageWriteServerRoutesConfigurator(
|
||||
storage,
|
||||
RolesManagerRole.serializer(),
|
||||
RolesManagerRolesChecker.key,
|
||||
unifiedRouter = unifiedRouter
|
||||
RolesManagerRolesChecker.key
|
||||
)
|
||||
|
@ -12,8 +12,7 @@ class RolesStorageWriteServerRoutesConfigurator<T : Role>(
|
||||
private val storage: WriteRolesStorage<T>,
|
||||
private val serializer: KSerializer<T>,
|
||||
private val includeAuthKey: String,
|
||||
private val excludeAuthKey: String = includeAuthKey,
|
||||
private val unifiedRouter: UnifiedRouter
|
||||
private val excludeAuthKey: String = includeAuthKey
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
override fun Route.invoke() {
|
||||
route(usersRolesRootPathPart) {
|
||||
|
@ -11,8 +11,7 @@ import kotlinx.serialization.builtins.serializer
|
||||
|
||||
class RolesStorageReadServerRoutesConfigurator<T : Role>(
|
||||
private val storage: ReadRolesStorage<T>,
|
||||
private val serializer: KSerializer<T>,
|
||||
private val unifiedRouter: UnifiedRouter
|
||||
private val serializer: KSerializer<T>
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
private val userRolesSerializer = ListSerializer(serializer)
|
||||
override fun Route.invoke() {
|
||||
|
@ -9,10 +9,10 @@ import kotlinx.serialization.builtins.nullable
|
||||
|
||||
class UsersStorageKtorClient(
|
||||
baseUrl: String,
|
||||
unifiedRequester: UnifiedRequester
|
||||
client: HttpClient
|
||||
) : ReadUsersStorage, ReadCRUDRepo<User, UserId> by KtorReadStandardCrudRepo(
|
||||
buildStandardUrl(baseUrl, usersServerPathPart),
|
||||
unifiedRequester,
|
||||
client,
|
||||
User.serializer(),
|
||||
User.serializer().nullable,
|
||||
UserId.serializer()
|
||||
|
@ -1,28 +1,21 @@
|
||||
package dev.inmo.postssystem.features.users.server
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
||||
import dev.inmo.postssystem.features.users.common.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadStandardCrudRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadCRUDRepoRoutes
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
|
||||
class UsersStorageServerRoutesConfigurator(
|
||||
private val usersStorage: ReadUsersStorage,
|
||||
private val unifiedRouter: UnifiedRouter
|
||||
private val usersStorage: ReadUsersStorage
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
override fun Route.invoke() {
|
||||
authenticate {
|
||||
route(usersServerPathPart) {
|
||||
configureReadStandardCrudRepoRoutes(
|
||||
usersStorage,
|
||||
User.serializer(),
|
||||
User.serializer().nullable,
|
||||
UserId.serializer(),
|
||||
unifiedRouter
|
||||
)
|
||||
configureReadCRUDRepoRoutes(
|
||||
usersStorage
|
||||
) { UserId(it.toLong()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import kotlinx.serialization.builtins.serializer
|
||||
|
||||
class SimplePublicatorServiceClient(
|
||||
baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester
|
||||
private val client: HttpClient
|
||||
) : SimplePublicatorService {
|
||||
private val fullUrl = buildStandardUrl(
|
||||
baseUrl,
|
||||
|
@ -18,11 +18,10 @@ import dev.inmo.postssystem.features.status.server.StatusRoutingConfigurator
|
||||
import dev.inmo.postssystem.features.users.common.ExposedUsersStorage
|
||||
import dev.inmo.postssystem.features.users.server.UsersStorageServerRoutesConfigurator
|
||||
import dev.inmo.micro_utils.coroutines.LinkedSupervisorScope
|
||||
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.*
|
||||
import dev.inmo.micro_utils.ktor.server.createKtorServer
|
||||
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedOneToManyKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedKeyValuesRepo
|
||||
import dev.inmo.postssystem.features.common.common.*
|
||||
import dev.inmo.postssystem.features.common.server.Qualifiers
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
@ -88,8 +87,6 @@ fun getDIModule(
|
||||
}
|
||||
}
|
||||
|
||||
single { UnifiedRouter(get()) }
|
||||
|
||||
singleWithBinds { config }
|
||||
singleWithBinds { get<Config>().databaseConfig }
|
||||
singleWithBinds { get<Config>().authConfig }
|
||||
@ -101,7 +98,7 @@ fun getDIModule(
|
||||
singleWithBinds { exposedUsersAuthenticator(get(), get()) }
|
||||
|
||||
factory<KeyValuesRolesOriginalRepo>(Qualifiers.usersRolesKeyValueFactoryQualifier) { (tableName: String) ->
|
||||
ExposedOneToManyKeyValueRepo(get(), { text("subject") }, { text("role") }, tableName)
|
||||
ExposedKeyValuesRepo(get(), { text("subject") }, { text("role") }, tableName)
|
||||
}
|
||||
single {
|
||||
RolesManagerRoleStorage(get(Qualifiers.usersRolesKeyValueFactoryQualifier) { ParametersHolder(mutableListOf("rolesManager")) })
|
||||
@ -154,12 +151,12 @@ fun getDIModule(
|
||||
}
|
||||
|
||||
// Routing configurators
|
||||
singleWithBinds { FilesRoutingConfigurator(get(), null, get()) }
|
||||
singleWithBinds { FilesRoutingConfigurator(get(), null) }
|
||||
singleWithBinds { StatusRoutingConfigurator }
|
||||
singleWithBinds { UsersStorageServerRoutesConfigurator(get(), get()) }
|
||||
singleWithBinds { RolesStorageReadServerRoutesConfigurator<Role>(get(), RoleSerializer, get()) }
|
||||
singleWithBinds { RolesManagerRolesStorageServerRoutesConfigurator(get(), get()) }
|
||||
singleWithBinds { ServerPostsServiceRoutingConfigurator(get(), get(), get(), get()) }
|
||||
singleWithBinds { UsersStorageServerRoutesConfigurator(get()) }
|
||||
singleWithBinds { RolesStorageReadServerRoutesConfigurator<Role>(get(), RoleSerializer) }
|
||||
singleWithBinds { RolesManagerRolesStorageServerRoutesConfigurator(get()) }
|
||||
singleWithBinds { ServerPostsServiceRoutingConfigurator(get(), get(), get()) }
|
||||
|
||||
singleWithBinds { ClientStaticRoutingConfiguration("web") }
|
||||
singleWithBinds {
|
||||
@ -181,7 +178,7 @@ fun getDIModule(
|
||||
get()
|
||||
)
|
||||
}
|
||||
singleWithBinds { AuthenticationRoutingConfigurator(get(), get(), get()) }
|
||||
singleWithBinds { AuthenticationRoutingConfigurator(get(), get()) }
|
||||
singleWithBinds { NotFoundStatusPageRedirectToIndex("/") }
|
||||
|
||||
if (config.debugMode) {
|
||||
|
@ -5,7 +5,7 @@ import dev.inmo.postssystem.services.posts.common.*
|
||||
|
||||
class ClientPostsService(
|
||||
baseUrl: String,
|
||||
unifiedRequester: UnifiedRequester
|
||||
client: HttpClient
|
||||
) : PostsService,
|
||||
ReadPostsService by ClientReadPostsService(baseUrl, unifiedRequester),
|
||||
WritePostsService by ClientWritePostsService(baseUrl, unifiedRequester)
|
||||
ReadPostsService by ClientReadPostsService(baseUrl, client),
|
||||
WritePostsService by ClientWritePostsService(baseUrl, client)
|
||||
|
@ -12,10 +12,10 @@ import kotlinx.serialization.builtins.nullable
|
||||
|
||||
class ClientReadPostsService(
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester
|
||||
private val client: HttpClient
|
||||
) : ReadPostsService, ReadCRUDRepo<RegisteredPost, PostId> by KtorReadStandardCrudRepo(
|
||||
buildStandardUrl(baseUrl, postsRootPath),
|
||||
unifiedRequester,
|
||||
client,
|
||||
RegisteredPost.serializer(),
|
||||
RegisteredPost.serializer().nullable,
|
||||
PostId.serializer()
|
||||
|
@ -25,17 +25,17 @@ import kotlinx.serialization.modules.polymorphic
|
||||
|
||||
class ClientWritePostsService(
|
||||
private val baseUrl: String,
|
||||
unifiedRequester: UnifiedRequester
|
||||
client: HttpClient
|
||||
) : WritePostsService {
|
||||
private val root = buildStandardUrl(baseUrl, postsRootPath)
|
||||
private val unifiedRequester = UnifiedRequester(
|
||||
unifiedRequester.client,
|
||||
unifiedRequester.serialFormat.createWithSerializerModuleExtension {
|
||||
polymorphic(Content::class) {
|
||||
subclass(BinaryContent::class, BinaryContentSerializer(TempFileIdentifierInputProvider::class, TempFileIdentifierInputProvider.serializer()))
|
||||
}
|
||||
}
|
||||
)
|
||||
// private val unifiedRequester = UnifiedRequester(
|
||||
// unifiedRequester.client,
|
||||
// unifiedRequester.serialFormat.createWithSerializerModuleExtension {
|
||||
// polymorphic(Content::class) {
|
||||
// subclass(BinaryContent::class, BinaryContentSerializer(TempFileIdentifierInputProvider::class, TempFileIdentifierInputProvider.serializer()))
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
private val contentEitherSerializer = EitherSerializer(ContentId.serializer(), ContentSerializer)
|
||||
private val contentsEitherSerializer = ListSerializer(contentEitherSerializer)
|
||||
@ -58,7 +58,7 @@ class ClientWritePostsService(
|
||||
return (content as? BinaryContent) ?.let {
|
||||
when (val provider = it.inputProvider) {
|
||||
is FileBasedInputProvider -> {
|
||||
val fileId = unifiedRequester.tempUpload(
|
||||
val fileId = client.tempUpload(
|
||||
tempUploadFullPath,
|
||||
provider.file
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import dev.inmo.micro_utils.mime_types.findBuiltinMimeType
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.createRouting
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.updateRouting
|
||||
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.removeRoute
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadStandardCrudRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadCRUDRepoRoutes
|
||||
import dev.inmo.postssystem.features.common.common.FileBasedInputProvider
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
@ -22,6 +22,7 @@ import io.ktor.http.content.streamProvider
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.request.receiveMultipart
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
@ -32,8 +33,6 @@ import io.ktor.utils.io.streams.asInput
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.builtins.*
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.attribute.FileTime
|
||||
@ -42,19 +41,8 @@ import java.util.concurrent.TimeUnit
|
||||
class ServerPostsServiceRoutingConfigurator(
|
||||
private val readPostsService: ReadPostsService,
|
||||
private val writePostsService: WritePostsService? = readPostsService as? WritePostsService,
|
||||
private val scope: CoroutineScope,
|
||||
unifiedRouter: UnifiedRouter
|
||||
private val scope: CoroutineScope
|
||||
) : ApplicationRoutingConfigurator.Element {
|
||||
private val unifiedRouter = UnifiedRouter(
|
||||
serialFormat = unifiedRouter.serialFormat.createWithSerializerModuleExtension {
|
||||
polymorphic(Content::class) {
|
||||
subclass(BinaryContent::class, BinaryContentSerializer(TempFileIdentifierInputProvider::class, TempFileIdentifierInputProvider.serializer()))
|
||||
}
|
||||
}
|
||||
)
|
||||
private val contentEitherSerializer = EitherSerializer(ContentId.serializer(), ContentSerializer)
|
||||
private val contentsEitherSerializer = ListSerializer(contentEitherSerializer)
|
||||
private val contentsSerializer = ListSerializer(ContentSerializer)
|
||||
|
||||
private val temporalFilesMap = mutableMapOf<FileId, MPPFile>()
|
||||
private val temporalFilesMutex = Mutex()
|
||||
@ -97,20 +85,16 @@ class ServerPostsServiceRoutingConfigurator(
|
||||
}
|
||||
|
||||
private suspend fun PipelineContext<Unit, ApplicationCall>.receiveContents(): List<Content> {
|
||||
return unifiedRouter.run {
|
||||
uniload(contentsSerializer).mapNotNull {
|
||||
mapBinary(it as? BinaryContent ?: return@mapNotNull it)
|
||||
}
|
||||
return call.receive<ContentsWrapper>().content.mapNotNull {
|
||||
mapBinary(it as? BinaryContent ?: return@mapNotNull it)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun PipelineContext<Unit, ApplicationCall>.receiveContentsEithers(): List<Either<ContentId, Content>> {
|
||||
return unifiedRouter.run {
|
||||
uniload(contentsEitherSerializer).mapNotNull {
|
||||
it.mapOnSecond {
|
||||
mapBinary(it as? BinaryContent ?: return@mapOnSecond null) ?.either()
|
||||
} ?: it
|
||||
}
|
||||
return call.receive<ContentsEithersWrapper>().content.mapNotNull {
|
||||
it.mapOnSecond {
|
||||
mapBinary(it as? BinaryContent ?: return@mapOnSecond null) ?.either()
|
||||
} ?: it
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,84 +103,70 @@ class ServerPostsServiceRoutingConfigurator(
|
||||
override fun Route.invoke() {
|
||||
authenticate {
|
||||
route(postsRootPath) {
|
||||
configureReadStandardCrudRepoRoutes(
|
||||
readPostsService,
|
||||
RegisteredPost.serializer(),
|
||||
RegisteredPost.serializer().nullable,
|
||||
PostId.serializer(),
|
||||
unifiedRouter
|
||||
)
|
||||
configureReadCRUDRepoRoutes(
|
||||
readPostsService
|
||||
) { PostId(it.toLong()) }
|
||||
|
||||
writePostsService ?.let {
|
||||
|
||||
unifiedRouter.apply {
|
||||
post(createRouting) {
|
||||
val data = receiveContents()
|
||||
|
||||
post(createRouting) {
|
||||
val data = receiveContents()
|
||||
call.respond(
|
||||
writePostsService.create(FullNewPost(data)) ?: HttpStatusCode.NoContent
|
||||
)
|
||||
}
|
||||
|
||||
unianswer(
|
||||
RegisteredPost.serializer().nullable,
|
||||
writePostsService.create(FullNewPost(data))
|
||||
post(updateRouting) {
|
||||
call.respond(
|
||||
writePostsService.update(
|
||||
call.getQueryParameterOrSendError(postsPostIdParameter)?.toLong()?.let(::PostId) ?: return@post,
|
||||
receiveContentsEithers()
|
||||
) ?: HttpStatusCode.NoContent
|
||||
)
|
||||
}
|
||||
|
||||
post(removeRoute) {
|
||||
call.respond(
|
||||
writePostsService.remove(
|
||||
call.receive()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
post(updateRouting) {
|
||||
val postId = call.decodeUrlQueryValueOrSendError(postsPostIdParameter, PostId.serializer()) ?: return@post
|
||||
val data = receiveContentsEithers()
|
||||
post(postsCreateTempPathPart) {
|
||||
val multipart = call.receiveMultipart()
|
||||
|
||||
unianswer(
|
||||
RegisteredPost.serializer().nullable,
|
||||
writePostsService.update(
|
||||
postId,
|
||||
data
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
post(removeRoute) {
|
||||
val postId = uniload(PostId.serializer())
|
||||
|
||||
unianswer(
|
||||
Unit.serializer(),
|
||||
writePostsService.remove(postId)
|
||||
)
|
||||
}
|
||||
|
||||
post(postsCreateTempPathPart) {
|
||||
val multipart = call.receiveMultipart()
|
||||
|
||||
var fileInfo: Pair<FileId, MPPFile>? = null
|
||||
var part = multipart.readPart()
|
||||
while (part != null) {
|
||||
if (part is PartData.FileItem) {
|
||||
break
|
||||
}
|
||||
part = multipart.readPart()
|
||||
var fileInfo: Pair<FileId, MPPFile>? = null
|
||||
var part = multipart.readPart()
|
||||
while (part != null) {
|
||||
if (part is PartData.FileItem) {
|
||||
break
|
||||
}
|
||||
part ?.let {
|
||||
if (it is PartData.FileItem) {
|
||||
val fileId = FileId(uuid4().toString())
|
||||
val fileName = it.originalFileName ?.let { FileName(it) } ?: return@let
|
||||
fileInfo = fileId to File.createTempFile(fileId.string, ".${fileName.extension}").apply {
|
||||
outputStream().use { outputStream ->
|
||||
it.streamProvider().use {
|
||||
it.copyTo(outputStream)
|
||||
}
|
||||
part = multipart.readPart()
|
||||
}
|
||||
part ?.let {
|
||||
if (it is PartData.FileItem) {
|
||||
val fileId = FileId(uuid4().toString())
|
||||
val fileName = it.originalFileName ?.let { FileName(it) } ?: return@let
|
||||
fileInfo = fileId to File.createTempFile(fileId.string, ".${fileName.extension}").apply {
|
||||
outputStream().use { outputStream ->
|
||||
it.streamProvider().use {
|
||||
it.copyTo(outputStream)
|
||||
}
|
||||
deleteOnExit()
|
||||
}
|
||||
deleteOnExit()
|
||||
}
|
||||
}
|
||||
|
||||
fileInfo ?.also { (fileId, file) ->
|
||||
temporalFilesMutex.withLock {
|
||||
temporalFilesMap[fileId] = file
|
||||
}
|
||||
call.respond(fileId.string)
|
||||
} ?: call.respond(HttpStatusCode.BadRequest)
|
||||
|
||||
}
|
||||
|
||||
fileInfo ?.also { (fileId, file) ->
|
||||
temporalFilesMutex.withLock {
|
||||
temporalFilesMap[fileId] = file
|
||||
}
|
||||
call.respond(fileId.string)
|
||||
} ?: call.respond(HttpStatusCode.BadRequest)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user