Commit 97a854e8 by Aleksandr Tamakov

Rearrange modules

parent 6fcf06a5
......@@ -18,6 +18,7 @@ annotation class JobStatus {
const val IDLE = 0
const val RENDER_UPLOAD = 9
const val READY_TO_PRINT = 10
const val UPLOAD_ERROR = 11
}
}
......@@ -36,5 +37,6 @@ val Int.statusName
JobStatus.IDLE -> "IDLE"
JobStatus.RENDER_UPLOAD -> "RENDER_UPLOAD"
JobStatus.READY_TO_PRINT -> "READY_TO_PRINT"
JobStatus.UPLOAD_ERROR -> "UPLOAD_ERROR"
else -> "Unknown"
}
\ No newline at end of file
......@@ -14,18 +14,6 @@ class JobNetworkSource(private val api: ApiJob) {
return response.body()?.transform()
}
fun uploadPage(jobId: String, token: String, filePath: String): Boolean {
val file = File(filePath)
val requestBody = ProgressEmittingRequestBody("image/*", file)
val fileBody = MultipartBody.Part.createFormData("file", file.name, requestBody)
val response = api.uploadPage(jobId = jobId, token = token, fileBody).execute()
return response.isSuccessful
}
fun complete(jobId: String, token: String): Boolean {
api.complete(jobId = jobId, token = token).execute()
return true
}
}
\ No newline at end of file
......@@ -16,19 +16,4 @@ import retrofit2.http.Path
interface ApiJob {
@POST("api/job")
fun createJob(@Body request: CreateJobRequest): Call<CreateJobResponse>
@Multipart
@POST("api/job/{jobId}")
fun uploadPage(
@Path("jobId") jobId: String,
@Header("X-Access-Token") token: String,
@Part body: MultipartBody.Part
): Call<ResponseBody>
@PATCH("api/job/{jobId}")
fun complete(
@Path("jobId") jobId: String,
@Header("X-Access-Token") token: String,
): Call<ResponseBody>
}
\ No newline at end of file
......@@ -50,7 +50,7 @@ internal object JobModule {
): Interceptor = MockInterceptor(context, isMock)
@Singleton @Provides
fun provideApiSpot(
fun provideApiJob(
@ApiServerUrl serverUrl: String,
@DiDebuggableBuild isDebugBuild: Boolean,
@ApiTokenInterceptor authInterceptor: Interceptor,
......
......@@ -5,6 +5,7 @@ import com.isidroid.job.model.PrintJob
import java.io.File
interface JobRepository {
suspend fun insert(vararg printJob: PrintJob)
suspend fun createJob(
name: String,
clientName: String,
......@@ -18,6 +19,5 @@ interface JobRepository {
suspend fun readLocalList(spotId: String? = null, @JobStatus status: Int? = null, ids: List<String>? = null): Collection<PrintJob>
suspend fun updateJobStatus(@JobStatus status: Int, vararg ids: String)
suspend fun uploadPage(jobId: String, token: String, filePath: String): Boolean
suspend fun completeUpload(jobId: String, token: String): PrintJob?
}
\ No newline at end of file
package com.isidroid.job.repository
import androidx.compose.runtime.produceState
import com.isidroid.job.CreateJobException
import com.isidroid.job.constant.JobStatus
import com.isidroid.job.data.source.local.JobLocalSource
......@@ -11,8 +12,9 @@ import java.io.File
private val jobNetworkSource: JobNetworkSource,
private val jobLocalSource: JobLocalSource,
) : JobRepository {
override suspend fun insert(vararg printJob: PrintJob) = jobLocalSource.insert(*printJob)
override suspend fun createJob(
override suspend fun createJob(
name: String,
clientName: String,
spotId: String,
......@@ -32,11 +34,4 @@ import java.io.File
override suspend fun readLocalList(spotId: String?, status: Int?, ids: List<String>?) = jobLocalSource.find(spotId = spotId, status = status, ids = ids)
override suspend fun updateJobStatus(status: Int, vararg ids: String) = jobLocalSource.updateJobStatus(status, *ids)
override suspend fun uploadPage(jobId: String, token: String, filePath: String) = jobNetworkSource.uploadPage(jobId = jobId, token = token, filePath = filePath)
override suspend fun completeUpload(jobId: String, token: String): PrintJob? {
val printJob = jobLocalSource.find(ids = listOf(jobId)).firstOrNull()
val uploadResult = jobNetworkSource.complete(jobId = jobId, token = token)
return printJob?.copy(status = JobStatus.READY_TO_PRINT)?.also { jobLocalSource.insert(it) }?.takeIf { uploadResult }
}
}
\ No newline at end of file
package com.isidroid.job_sender.data.source.remote
import android.content.Context
import okhttp3.Interceptor
import okhttp3.Response
class MockInterceptor(context: Context, isMock: Boolean) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(chain.request())
}
}
\ No newline at end of file
package com.isidroid.job_sender.data.source.remote
import com.isidroid.job_sender.data.source.remote.api.ApiCommand
import com.isidroid.job_sender.data.source.remote.api.ApiUpload
import com.isidroid.network.ProgressEmittingRequestBody
import okhttp3.MultipartBody
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SendJobNetworkSource @Inject constructor(
private val apiUpload: ApiUpload,
private val apiCommand: ApiCommand
) {
fun uploadPage(jobId: String, token: String, filePath: String): Boolean {
val file = File(filePath)
val requestBody = ProgressEmittingRequestBody("image/*", file)
val fileBody = MultipartBody.Part.createFormData("file", file.name, requestBody)
val response = apiUpload.upload(jobId = jobId, token = token, fileBody).execute()
return response.isSuccessful
}
fun complete(jobId: String, token: String): Boolean {
apiCommand.complete(jobId = jobId, token = token).execute()
return true
}
}
\ No newline at end of file
package com.isidroid.job_sender.data.source.remote.api
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.Header
import retrofit2.http.PATCH
import retrofit2.http.Path
interface ApiCommand {
@PATCH("api/job/{jobId}")
fun complete(
@Path("jobId") jobId: String,
@Header("X-Access-Token") token: String,
): Call<ResponseBody>
}
\ No newline at end of file
package com.isidroid.job_sender.data.source.remote.api
import okhttp3.MultipartBody
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Path
interface ApiUpload {
@Multipart
@POST("api/job/{jobId}")
fun upload(
@Path("jobId") jobId: String,
@Header("X-Access-Token") token: String,
@Part body: MultipartBody.Part
): Call<ResponseBody>
}
\ No newline at end of file
package com.isidroid.job_sender.di
import android.content.Context
import com.google.gson.Gson
import com.isidroid.core.DiDebuggableBuild
import com.isidroid.core.DiMock
import com.isidroid.job.data.source.remote.api.ApiJob
import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.SendJobEventCollectorFlow
import com.isidroid.job_sender.data.source.local.SendJobDao
import com.isidroid.job_sender.data.source.local.SendJobLocalSource
import com.isidroid.job_sender.data.source.remote.MockInterceptor
import com.isidroid.job_sender.data.source.remote.SendJobNetworkSource
import com.isidroid.job_sender.data.source.remote.api.ApiCommand
import com.isidroid.job_sender.data.source.remote.api.ApiUpload
import com.isidroid.job_sender.repository.JobSendRepository
import com.isidroid.job_sender.repository.JobSendRepositoryImpl
import com.isidroid.network.ApiCacheControlInterceptor
import com.isidroid.network.ApiServerUrl
import com.isidroid.network.ApiTokenInterceptor
import com.isidroid.network.createRetrofitApiClient
import com.isidroid.rendering.repository.RenderRepository
import com.isidroid.spot.repository.SpotRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.Interceptor
import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Qualifier
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
......@@ -21,11 +38,61 @@ object JobSenderModule {
fun provideSendJobLocalSource(dao: SendJobDao) = SendJobLocalSource(dao)
@Provides @Singleton
fun provideSendJobNetworkSource(apiUpload: ApiUpload, apiCommand: ApiCommand) = SendJobNetworkSource(apiUpload, apiCommand)
@Provides @Singleton
fun provideJobSendRepository(
renderRepository: RenderRepository,
spotRepository: SpotRepository,
printJobRepository: JobRepository,
sendJobLocalSource: SendJobLocalSource,
sendJobNetworkSource: SendJobNetworkSource,
eventCollector: SendJobEventCollectorFlow,
): JobSendRepository = JobSendRepositoryImpl(renderRepository, spotRepository, printJobRepository, sendJobLocalSource, eventCollector)
}
\ No newline at end of file
): JobSendRepository = JobSendRepositoryImpl(renderRepository, spotRepository, printJobRepository, sendJobLocalSource, sendJobNetworkSource, eventCollector)
@Provides @DiJobSenderMockInterceptor
fun provideMockInterceptor(
@ApplicationContext context: Context,
@DiMock isMock: Boolean
): Interceptor = MockInterceptor(context, isMock)
@Singleton @Provides
fun provideApiCommand(
@ApiServerUrl serverUrl: String,
@DiDebuggableBuild isDebugBuild: Boolean,
@ApiTokenInterceptor authInterceptor: Interceptor,
@DiJobSenderMockInterceptor mockInterceptor: Interceptor,
@ApiCacheControlInterceptor cacheInterceptor: Interceptor,
gson: Gson
) = createRetrofitApiClient(
baseUrl = serverUrl,
cl = ApiCommand::class.java,
logLevel = HttpLoggingInterceptor.Level.BODY,
gson = gson,
isDebugBuild = isDebugBuild,
interceptors = listOf(authInterceptor, mockInterceptor, cacheInterceptor)
)
@Singleton @Provides
fun provideApiUpload(
@ApiServerUrl serverUrl: String,
@DiDebuggableBuild isDebugBuild: Boolean,
@ApiTokenInterceptor authInterceptor: Interceptor,
@DiJobSenderMockInterceptor mockInterceptor: Interceptor,
@ApiCacheControlInterceptor cacheInterceptor: Interceptor,
gson: Gson
) = createRetrofitApiClient(
baseUrl = serverUrl,
cl = ApiUpload::class.java,
logLevel = HttpLoggingInterceptor.Level.HEADERS,
gson = gson,
isDebugBuild = isDebugBuild,
interceptors = listOf(authInterceptor, mockInterceptor, cacheInterceptor)
)
}
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
internal annotation class DiJobSenderMockInterceptor
\ No newline at end of file
......@@ -4,6 +4,8 @@ import com.isidroid.job.model.PrintJob
import com.isidroid.job_sender.domain.model.PrintJobSender
interface JobSendRepository {
suspend fun completeUpload(jobId: String, token: String): PrintJob?
suspend fun getJobList(status: Int): Collection<PrintJob>
suspend fun getJobSenderList(status: Int): Collection<PrintJobSender>
suspend fun checkNotRenderedPrintJobs(items: Collection<PrintJob>): Collection<PrintJobSender>?
......
......@@ -6,12 +6,12 @@ import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.SendJobEventCollectorFlow
import com.isidroid.job_sender.UploadPageException
import com.isidroid.job_sender.data.source.local.SendJobLocalSource
import com.isidroid.job_sender.data.source.remote.SendJobNetworkSource
import com.isidroid.job_sender.domain.model.PrintJobSender
import com.isidroid.job_sender.ext.createRenderItems
import com.isidroid.job_sender.ext.decreaseCounter
import com.isidroid.rendering.repository.RenderRepository
import com.isidroid.spot.repository.SpotRepository
import kotlinx.coroutines.delay
import timber.log.Timber
internal class JobSendRepositoryImpl(
......@@ -19,9 +19,18 @@ internal class JobSendRepositoryImpl(
private val spotRepository: SpotRepository,
private val printJobRepository: JobRepository,
private val sendJobLocalSource: SendJobLocalSource,
private val sendJobNetworkSource: SendJobNetworkSource,
private val eventCollector: SendJobEventCollectorFlow,
) : JobSendRepository {
override suspend fun completeUpload(jobId: String, token: String): PrintJob? {
val printJob = printJobRepository.readLocalList(ids = listOf(jobId)).firstOrNull()
val uploadResult = sendJobNetworkSource.complete(jobId = jobId, token = token)
return printJob?.copy(status = JobStatus.READY_TO_PRINT)?.also { printJobRepository.insert(it) }?.takeIf { uploadResult }
}
override suspend fun getJobList(status: Int): Collection<PrintJob> = printJobRepository.readLocalList(status = status)
override suspend fun getJobSenderList(status: Int): Collection<PrintJobSender> = sendJobLocalSource.findByStatus(status)
......@@ -49,8 +58,7 @@ internal class JobSendRepositoryImpl(
eventCollector.updateProgress(index, total)
try {
val uploadResult = printJobRepository.uploadPage(jobId = item.printJobId, token = item.accessToken, filePath = item.sourceFile)
val uploadResult = sendJobNetworkSource.uploadPage(jobId = item.printJobId, token = item.accessToken, filePath = item.sourceFile)
if (!uploadResult)
throw UploadPageException()
......@@ -65,21 +73,30 @@ internal class JobSendRepositoryImpl(
eventCollector.updateProgress(total, total)
// update jobs
val jobsIds = sendJobResults.filter { it.value == 0 }.keys
printJobRepository.updateJobStatus(status = JobStatus.RENDER_UPLOAD, *jobsIds.toTypedArray())
val successJobIds = sendJobResults.filter { it.value == 0 }.keys
printJobRepository.updateJobStatus(status = JobStatus.RENDER_UPLOAD, *successJobIds.toTypedArray())
val failedJobIds = sendJobResults.filter { it.value != 0 }.keys
printJobRepository.updateJobStatus(status = JobStatus.UPLOAD_ERROR, *failedJobIds.toTypedArray())
val jobs = printJobRepository.readLocalList(ids = jobsIds.toList())
val successJobs = printJobRepository.readLocalList(ids = successJobIds.toList())
println("failedJobIds=$failedJobIds")
val failedJobs = printJobRepository.readLocalList(ids = failedJobIds.toList())
// notify event emitters
eventCollector.updateStatus(status = JobStatus.RENDER_UPLOAD, *jobs.toTypedArray())
if (successJobs.isNotEmpty())
eventCollector.updateStatus(status = JobStatus.RENDER_UPLOAD, *successJobs.toTypedArray())
if (failedJobs.isNotEmpty())
eventCollector.updateStatus(status = JobStatus.UPLOAD_ERROR, *failedJobs.toTypedArray())
return jobs
return successJobs
}
override suspend fun markJobUploaded(items: Collection<PrintJob>): Collection<PrintJob> {
return items.mapNotNull { job ->
// upload
val updateJob = printJobRepository.completeUpload(jobId = job.id, token = job.accessToken.orEmpty())
val updateJob = completeUpload(jobId = job.id, token = job.accessToken.orEmpty())
// notify event emitters
eventCollector.updateStatus(status = JobStatus.READY_TO_PRINT, updateJob)
......
......@@ -8,6 +8,7 @@ import com.isidroid.job.data.source.remote.api.ApiJob
import com.isidroid.job.model.PrintJob
import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.data.source.local.SendJobLocalSource
import com.isidroid.job_sender.data.source.remote.SendJobNetworkSource
import com.isidroid.job_sender.domain.dto.JobSenderResult
import com.isidroid.job_sender.domain.model.PrintJobSender
import com.isidroid.job_sender.repository.JobSendRepositoryImpl
......@@ -41,9 +42,8 @@ class UploadJobsTest {
private lateinit var repository: JobSendRepositoryImpl
private lateinit var spotRepository: SpotRepository
private lateinit var sendJobLocalSource: SendJobLocalSource
private lateinit var sendJobNetworkSource: SendJobNetworkSource
private lateinit var eventCollector: SendJobEventCollectorFlow
private lateinit var jobLocalSource: JobLocalSource
private lateinit var jobNetworkSource: JobNetworkSource
private val mockWrapper = MockWebServerWrapper { path, method, body ->
when {
......@@ -58,13 +58,12 @@ class UploadJobsTest {
api = createApi(mockWebServer, ApiJob::class.java)
renderRepository = mock(RenderRepository::class.java)
spotRepository = mock(SpotRepository::class.java)
jobLocalSource = mock(JobLocalSource::class.java)
jobNetworkSource = JobNetworkSource(api)
printJobRepository = mock(JobRepository::class.java)
sendJobLocalSource = mock(SendJobLocalSource::class.java)
sendJobNetworkSource = mock(SendJobNetworkSource::class.java)
eventCollector = SendJobEventCollectorFlow()
repository = JobSendRepositoryImpl(renderRepository, spotRepository, printJobRepository, sendJobLocalSource, eventCollector)
repository = JobSendRepositoryImpl(renderRepository, spotRepository, printJobRepository, sendJobLocalSource, sendJobNetworkSource, eventCollector)
}
@After
......@@ -83,7 +82,7 @@ class UploadJobsTest {
Mockito.`when`(printJobRepository.readLocalList(ids = items.map { it.printJobId }.toList())).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = emptyList())).thenReturn(emptyList())
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "", "")).thenReturn(true)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "", "")).thenReturn(true)
val events = mutableListOf<JobSenderResult>()
val deferred = async { eventCollector.eventsFlow.toList(events) }
......@@ -113,7 +112,7 @@ class UploadJobsTest {
Mockito.`when`(printJobRepository.readLocalList(ids = items.map { it.printJobId }.distinct().toList())).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = emptyList())).thenReturn(emptyList())
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "", "")).thenReturn(true)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "", "")).thenReturn(true)
val events = mutableListOf<JobSenderResult>()
val deferred = async { eventCollector.eventsFlow.toList(events) }
......@@ -145,7 +144,7 @@ class UploadJobsTest {
Mockito.`when`(printJobRepository.readLocalList(ids = items.map { it.printJobId }.toList())).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = emptyList())).thenReturn(emptyList())
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "", "")).thenReturn(false)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "", "")).thenReturn(false)
val events = mutableListOf<JobSenderResult>()
val deferred = async { eventCollector.eventsFlow.toList(events) }
......@@ -179,8 +178,8 @@ class UploadJobsTest {
Mockito.`when`(printJobRepository.readLocalList(ids = items.map { it.printJobId }.distinct().toList())).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = emptyList())).thenReturn(emptyList())
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "1", "")).thenReturn(true)
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "2", "")).thenReturn(false)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "1", "")).thenReturn(true)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "2", "")).thenReturn(false)
val events = mutableListOf<JobSenderResult>()
val deferred = async { eventCollector.eventsFlow.toList(events) }
......@@ -220,10 +219,11 @@ class UploadJobsTest {
Mockito.`when`(printJobRepository.readLocalList(ids = items.map { it.printJobId }.distinct().toList())).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = listOf(jobId2))).thenReturn(jobs)
Mockito.`when`(printJobRepository.readLocalList(ids = listOf("jobId"))).thenReturn(jobs.filter { it.accessToken == "2" })
Mockito.`when`(printJobRepository.readLocalList(ids = emptyList())).thenReturn(emptyList())
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "1", "")).thenReturn(true)
Mockito.`when`(printJobRepository.uploadPage(printJobSender.printJobId, "2", "")).thenReturn(false)
Mockito.`when`(printJobRepository.uploadPage(jobId2, "3", "")).thenReturn(true)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "1", "")).thenReturn(true)
Mockito.`when`(sendJobNetworkSource.uploadPage(printJobSender.printJobId, "2", "")).thenReturn(false)
Mockito.`when`(sendJobNetworkSource.uploadPage(jobId2, "3", "")).thenReturn(true)
val events = mutableListOf<JobSenderResult>()
val deferred = async { eventCollector.eventsFlow.toList(events) }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment