dev #1
@@ -0,0 +1,90 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.app
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.constants.Constants
 | 
			
		||||
import net.xintanalabs.rssotto.model.App
 | 
			
		||||
import net.xintanalabs.rssotto.service.AppService
 | 
			
		||||
import org.springframework.http.HttpStatus
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController
 | 
			
		||||
import org.springframework.web.server.ResponseStatusException
 | 
			
		||||
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("${Constants.API_BASE_PATH}/app")
 | 
			
		||||
class AppController(
 | 
			
		||||
    private val appService: AppService
 | 
			
		||||
) {
 | 
			
		||||
    @PostMapping
 | 
			
		||||
    fun createApp(@RequestBody app: AppRequest): AppResponse? =
 | 
			
		||||
        appService.createApp(app.toModel())
 | 
			
		||||
            ?.toResponse()
 | 
			
		||||
            ?:  throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot create app")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    fun getApp(@PathVariable id: String): AppResponse? =
 | 
			
		||||
         appService.getAppById(id)
 | 
			
		||||
            ?.toResponse()
 | 
			
		||||
            ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "App not found")
 | 
			
		||||
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    fun  getAllApps(): List<AppResponse> =
 | 
			
		||||
        appService.getAllApps()
 | 
			
		||||
            .map { it.toResponse() }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/versions")
 | 
			
		||||
    fun getAppsVersions(@RequestParam version: List<String>): List<AppVersionResponse> {
 | 
			
		||||
        return appService.getAppsByVersions(version).map { it.toVersionResponse() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/search")
 | 
			
		||||
    fun searchApps(@RequestParam q: String): List<AppResponse> {
 | 
			
		||||
        return appService.searchApps(q).map { it.toResponse() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun App.toVersionResponse(): AppVersionResponse {
 | 
			
		||||
        return AppVersionResponse(
 | 
			
		||||
            id = this.id,
 | 
			
		||||
            latestVersion = this.latestVersion,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun App.toResponse(): AppResponse {
 | 
			
		||||
        return AppResponse(
 | 
			
		||||
            id = this.id,
 | 
			
		||||
            uid = this.uid,
 | 
			
		||||
            name = this.name,
 | 
			
		||||
            type = this.type,
 | 
			
		||||
            source = this.source,
 | 
			
		||||
            params = this.params,
 | 
			
		||||
            fields = this.fields,
 | 
			
		||||
            downloadUrl = this.downloadUrl,
 | 
			
		||||
            latestVersion = this.latestVersion,
 | 
			
		||||
            createdAt = this.createdAt,
 | 
			
		||||
            updatedAt = this.updatedAt,
 | 
			
		||||
            lastCheckedAt = this.lastCheckedAt,
 | 
			
		||||
            active = this.active
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun AppRequest.toModel(): App {
 | 
			
		||||
        return App(
 | 
			
		||||
            uid = this.uid,
 | 
			
		||||
            name = this.name,
 | 
			
		||||
            type = this.type,
 | 
			
		||||
            source = this.source,
 | 
			
		||||
            params = this.params,
 | 
			
		||||
            fields = this.fields,
 | 
			
		||||
            downloadUrl =  this.downloadUrl,
 | 
			
		||||
            latestVersion = this.latestVersion,
 | 
			
		||||
            createdAt = System.currentTimeMillis(),
 | 
			
		||||
            updatedAt = System.currentTimeMillis(),
 | 
			
		||||
            lastCheckedAt = 0,
 | 
			
		||||
            active = true
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.app
 | 
			
		||||
 | 
			
		||||
data class AppRequest(
 | 
			
		||||
    val uid: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val type: String = "",
 | 
			
		||||
    val source: String,
 | 
			
		||||
    val params: Map<String, String>,
 | 
			
		||||
    val fields: Map<String, String>,
 | 
			
		||||
    val downloadUrl: String,
 | 
			
		||||
    val latestVersion: String? = null,
 | 
			
		||||
    val createdAt: Long = 0,
 | 
			
		||||
    val updatedAt: Long = 0,
 | 
			
		||||
    val lastCheckedAt: Long = 0,
 | 
			
		||||
    val active: Boolean = true
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.app
 | 
			
		||||
 | 
			
		||||
data class AppResponse(
 | 
			
		||||
    val id: String? = null,
 | 
			
		||||
    val uid: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val type: String = "",
 | 
			
		||||
    val source: String,
 | 
			
		||||
    val params: Map<String, String>,
 | 
			
		||||
    val fields: Map<String, String>,
 | 
			
		||||
    val downloadUrl: String,
 | 
			
		||||
    val latestVersion: String? = null,
 | 
			
		||||
    val createdAt: Long = 0,
 | 
			
		||||
    val updatedAt: Long = 0,
 | 
			
		||||
    val lastCheckedAt: Long = 0,
 | 
			
		||||
    val active: Boolean = true
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.app
 | 
			
		||||
 | 
			
		||||
import org.springframework.data.annotation.Id
 | 
			
		||||
 | 
			
		||||
data class AppVersionResponse(
 | 
			
		||||
    @Id val id: String? = null,
 | 
			
		||||
    val latestVersion: String? = null
 | 
			
		||||
)
 | 
			
		||||
@@ -25,9 +25,9 @@ class SourceController(private val sourceService: SourceService) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    fun getAll(): List<Source> {
 | 
			
		||||
    fun getAll(): List<SourceResponse> {
 | 
			
		||||
        return try {
 | 
			
		||||
            sourceService.findALl()
 | 
			
		||||
            sourceService.findALl().map { it.toResponse() }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Cannot retrieve sources: ${e.message}", e)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.type
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.constants.Constants
 | 
			
		||||
import net.xintanalabs.rssotto.model.Type
 | 
			
		||||
import net.xintanalabs.rssotto.service.TypeService
 | 
			
		||||
import org.springframework.http.HttpStatus
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController
 | 
			
		||||
import org.springframework.web.server.ResponseStatusException
 | 
			
		||||
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("${Constants.API_BASE_PATH}/type")
 | 
			
		||||
class TypeController(
 | 
			
		||||
    private val typeService: TypeService
 | 
			
		||||
) {
 | 
			
		||||
    @PostMapping
 | 
			
		||||
    fun createType(@RequestBody type: TypeRequest): TypeResponse? {
 | 
			
		||||
        return typeService.create(type.toModel())
 | 
			
		||||
            ?.toResponse()
 | 
			
		||||
            ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot create user")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    fun getAllTypes(): List<TypeResponse> {
 | 
			
		||||
        return typeService.findAll().map{ it.toResponse() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    fun getType(@PathVariable id: String): TypeResponse? {
 | 
			
		||||
        return typeService.findById(id)
 | 
			
		||||
            ?.toResponse()
 | 
			
		||||
            ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun TypeRequest.toModel(): Type =
 | 
			
		||||
        Type(
 | 
			
		||||
            id = null,
 | 
			
		||||
            shortName = this.shortName,
 | 
			
		||||
            name = this.name,
 | 
			
		||||
            configFields = this.configFields,
 | 
			
		||||
            appFields = this.appFields
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    private fun Type.toResponse(): TypeResponse =
 | 
			
		||||
        TypeResponse(
 | 
			
		||||
            id = this.id,
 | 
			
		||||
            shortName = this.shortName,
 | 
			
		||||
            name = this.name,
 | 
			
		||||
            configFields = this.configFields,
 | 
			
		||||
            appFields = this.appFields
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.type
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.model.Field
 | 
			
		||||
 | 
			
		||||
data class TypeRequest(
 | 
			
		||||
    val shortName: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val configFields: List<Field>,
 | 
			
		||||
    val appFields: List<Field>
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
package net.xintanalabs.rssotto.controller.type
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.model.Field
 | 
			
		||||
 | 
			
		||||
data class TypeResponse(
 | 
			
		||||
    val id: String? = null,
 | 
			
		||||
    val shortName: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val configFields: List<Field>,
 | 
			
		||||
    val appFields: List<Field>
 | 
			
		||||
)
 | 
			
		||||
@@ -26,8 +26,8 @@ class UserController(private val userService: UserService) {
 | 
			
		||||
            ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot create user")
 | 
			
		||||
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    fun listAll(): List<UserResponse> = listOf()
 | 
			
		||||
        //userService.findAll().map { it.toResponse() }
 | 
			
		||||
    fun listAll(): List<UserResponse> =
 | 
			
		||||
        userService.findAll().map { it.toResponse() }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/{uuid}")
 | 
			
		||||
    fun findById(@PathVariable uuid: UUID): UserResponse {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package net.xintanalabs.rssotto.db.mongodb
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired
 | 
			
		||||
import org.springframework.data.mongodb.core.query.Criteria
 | 
			
		||||
 | 
			
		||||
abstract class MongoDBAbstract<T: Any>  (
 | 
			
		||||
    private val mongoDBClient: MongoDBClient
 | 
			
		||||
@@ -28,4 +29,8 @@ abstract class MongoDBAbstract<T: Any>  (
 | 
			
		||||
    protected fun update(id: String, updateFields: Map<String, Any>): Long {
 | 
			
		||||
        return mongoDBClient.updateOne(collection, idField, id, updateFields, entityClass)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected fun findByCriteria(criteria: Criteria): List<T> {
 | 
			
		||||
        return mongoDBClient.findByFilter(collection, criteria, entityClass)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/main/kotlin/net/xintanalabs/rssotto/model/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/main/kotlin/net/xintanalabs/rssotto/model/App.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package net.xintanalabs.rssotto.model
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonInclude
 | 
			
		||||
import org.springframework.data.annotation.Id
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
data class App(
 | 
			
		||||
    @Id val id: String? = null,
 | 
			
		||||
    val uid: String,
 | 
			
		||||
    val name: String,
 | 
			
		||||
    val type: String = "",
 | 
			
		||||
    val source: String,
 | 
			
		||||
    val params: Map<String, String>,
 | 
			
		||||
    val fields: Map<String, String>,
 | 
			
		||||
    val downloadUrl: String,
 | 
			
		||||
    val latestVersion: String? = null,
 | 
			
		||||
    val createdAt: Long = 0,
 | 
			
		||||
    val updatedAt: Long = 0,
 | 
			
		||||
    val lastCheckedAt: Long = 0,
 | 
			
		||||
    val active: Boolean = true
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
package net.xintanalabs.rssotto.repository
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.constants.Constants
 | 
			
		||||
import net.xintanalabs.rssotto.db.mongodb.MongoDBAbstract
 | 
			
		||||
import net.xintanalabs.rssotto.db.mongodb.MongoDBClient
 | 
			
		||||
import net.xintanalabs.rssotto.model.App
 | 
			
		||||
import org.springframework.data.mongodb.core.query.Criteria
 | 
			
		||||
import org.springframework.stereotype.Repository
 | 
			
		||||
 | 
			
		||||
@Repository
 | 
			
		||||
class AppRepository(
 | 
			
		||||
    mongoDBClient: MongoDBClient
 | 
			
		||||
): MongoDBAbstract<App>(mongoDBClient) {
 | 
			
		||||
    override val collection: String = Constants.COLLECTION_APPS
 | 
			
		||||
    override val entityClass: Class<App> = App::class.java
 | 
			
		||||
 | 
			
		||||
    fun createApp(app: App): App {
 | 
			
		||||
        return create(app)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getAllApps(): List<App> {
 | 
			
		||||
        return getAll()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getAppById(id: String): App? {
 | 
			
		||||
        return getById(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun deleteApp(id: String): Long {
 | 
			
		||||
        return delete(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateApp(id: String, updateFields: Map<String, Any>): Long {
 | 
			
		||||
        return update(id, updateFields)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun findAppsByCriteria(criteria: Criteria): List<App> {
 | 
			
		||||
        return findByCriteria(criteria)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,4 +25,5 @@ class SourceRepository (
 | 
			
		||||
    fun getSourceById(id: String): Source? {
 | 
			
		||||
        return getById(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,19 +7,21 @@ import net.xintanalabs.rssotto.model.Type
 | 
			
		||||
import org.springframework.stereotype.Repository
 | 
			
		||||
 | 
			
		||||
@Repository
 | 
			
		||||
class TypeRepository(mongoDBClient: MongoDBClient): MongoDBAbstract<Type>(mongoDBClient) {
 | 
			
		||||
class TypeRepository(
 | 
			
		||||
    mongoDBClient: MongoDBClient
 | 
			
		||||
): MongoDBAbstract<Type>(mongoDBClient) {
 | 
			
		||||
    override val collection: String = Constants.COLLECTION_TYPES
 | 
			
		||||
    override val entityClass: Class<Type> = Type::class.java
 | 
			
		||||
 | 
			
		||||
    suspend fun createType(type: Type): Type {
 | 
			
		||||
    fun createType(type: Type): Type {
 | 
			
		||||
        return create(type)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun getAllTypes(): List<Type> {
 | 
			
		||||
    fun getAllTypes(): List<Type> {
 | 
			
		||||
        return getAll()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun getTypeById(id: String): Type? {
 | 
			
		||||
    fun getTypeById(id: String): Type? {
 | 
			
		||||
        return getById(id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
package net.xintanalabs.rssotto.service
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.model.App
 | 
			
		||||
import net.xintanalabs.rssotto.repository.AppRepository
 | 
			
		||||
import org.springframework.data.mongodb.core.query.Criteria
 | 
			
		||||
import org.springframework.stereotype.Service
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
class AppService(
 | 
			
		||||
    private val appRepository: AppRepository
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun createApp(app: App): App? {
 | 
			
		||||
        return appRepository.createApp(app)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getAllApps(): List<App> {
 | 
			
		||||
        return appRepository.getAllApps()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getAppById(id: String): App? {
 | 
			
		||||
        return appRepository.getAppById(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun deleteApp(id: String): Long {
 | 
			
		||||
        return appRepository.deleteApp(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateApp(id: String, updateFields: Map<String, Any>): Long {
 | 
			
		||||
        return appRepository.updateApp(id, updateFields)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateAppDetails(id: String, newName: String?): Long {
 | 
			
		||||
        val updateFields = mutableMapOf<String, Any>()
 | 
			
		||||
        newName?.let { updateFields["name"] = it }
 | 
			
		||||
        updateFields["updatedAt"] = System.currentTimeMillis()
 | 
			
		||||
        return appRepository.updateApp(id, updateFields)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateLatestVersion(appId: String, latestVersion: String): Long {
 | 
			
		||||
        val updateFields = mapOf<String, Any>(
 | 
			
		||||
            "latestVersion" to latestVersion
 | 
			
		||||
        )
 | 
			
		||||
        return appRepository.updateApp(appId, updateFields)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getAppsByVersions(versions: List<String>): List<App> {
 | 
			
		||||
        val criteria = Criteria.where("latestVersion").`in`(versions)
 | 
			
		||||
        return appRepository.findAppsByCriteria(criteria)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun searchApps(query: String): List<App> {
 | 
			
		||||
        val regex = ".*${Regex.escape(query)}.*"
 | 
			
		||||
        val criteria = Criteria.where("name").regex(regex, "i")
 | 
			
		||||
        return appRepository.findAppsByCriteria(criteria)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
package net.xintanalabs.rssotto.service
 | 
			
		||||
 | 
			
		||||
import net.xintanalabs.rssotto.model.Type
 | 
			
		||||
import net.xintanalabs.rssotto.repository.TypeRepository
 | 
			
		||||
import org.springframework.stereotype.Service
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
class TypeService(
 | 
			
		||||
    private val typeRepository: TypeRepository
 | 
			
		||||
) {
 | 
			
		||||
    fun create(type: Type): Type? {
 | 
			
		||||
        return typeRepository.createType(type)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun findAll(): List<Type> {
 | 
			
		||||
        return typeRepository.getAllTypes()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun findById(id: String): Type? {
 | 
			
		||||
        return typeRepository.getTypeById(id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user