From dd478bf90c93f659de9bcf1bf7f58a1828a9fe64 Mon Sep 17 00:00:00 2001 From: MayorJay Date: Sun, 11 Feb 2024 15:39:46 +0000 Subject: [PATCH 1/2] feat: Implement Workflow Overrides API --- src/main/kotlin/Novu.kt | 3 + src/main/kotlin/api/WorkflowOverrideApi.kt | 54 ++++ .../request/CreateWorkflowOverrideRequest.kt | 10 + .../dto/request/GetWorkflowOverrideRequest.kt | 6 + .../UpdateWorkflowOverrideRequest.kt | 8 + .../dto/workflowoverrides/WorkflowOverride.kt | 24 ++ .../extensions/WorkflowOverrideExtensions.kt | 51 ++++ src/test/kotlin/WorkflowOverrideApiTest.kt | 259 ++++++++++++++++++ 8 files changed, 415 insertions(+) create mode 100644 src/main/kotlin/api/WorkflowOverrideApi.kt create mode 100644 src/main/kotlin/dto/request/CreateWorkflowOverrideRequest.kt create mode 100644 src/main/kotlin/dto/request/GetWorkflowOverrideRequest.kt create mode 100644 src/main/kotlin/dto/workflowoverrides/UpdateWorkflowOverrideRequest.kt create mode 100644 src/main/kotlin/dto/workflowoverrides/WorkflowOverride.kt create mode 100644 src/main/kotlin/extensions/WorkflowOverrideExtensions.kt create mode 100644 src/test/kotlin/WorkflowOverrideApiTest.kt diff --git a/src/main/kotlin/Novu.kt b/src/main/kotlin/Novu.kt index f50a24d..1f27ec6 100644 --- a/src/main/kotlin/Novu.kt +++ b/src/main/kotlin/Novu.kt @@ -1,5 +1,6 @@ package co.novu +import api.WorkflowOverrideApi import co.novu.api.BlueprintsApi import co.novu.api.ChangesApi import co.novu.api.EnvironmentsApi @@ -68,4 +69,6 @@ class Novu( internal val tenantsApi by lazy { retrofitInstance.create(TenantsApi::class.java) } internal val organizationsApi by lazy { retrofitInstance.create(OrganizationsApi::class.java) } + + internal val workflowOverrideApi by lazy { retrofitInstance.create(WorkflowOverrideApi::class.java) } } diff --git a/src/main/kotlin/api/WorkflowOverrideApi.kt b/src/main/kotlin/api/WorkflowOverrideApi.kt new file mode 100644 index 0000000..9fd9e5d --- /dev/null +++ b/src/main/kotlin/api/WorkflowOverrideApi.kt @@ -0,0 +1,54 @@ +package api + +import co.novu.dto.response.PaginatedResponseWrapper +import co.novu.dto.response.ResponseWrapper +import dto.request.CreateWorkflowOverrideRequest +import dto.workflowoverrides.UpdateWorkflowOverrideRequest +import dto.workflowoverrides.WorkflowOverride +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Path +import retrofit2.http.QueryMap + +interface WorkflowOverrideApi { + + companion object { + const val ENDPOINT = "workflow-overrides" + } + + @POST(ENDPOINT) + suspend fun createWorkflowOverride(@Body request: CreateWorkflowOverrideRequest): Response> + + @GET(ENDPOINT) + @JvmSuppressWildcards + suspend fun getWorkflowOverrides(@QueryMap params: Map): Response> + + @GET("$ENDPOINT/workflows/{workflowId}/tenants/{tenantId}") + suspend fun getWorkflowOverride( + @Path("workflowId") workflowId: String, + @Path("tenantId") tenantId: String + ): Response> + + @GET("$ENDPOINT/{overrideId}") + suspend fun getWorkflowOverrideById(@Path("overrideId") overrideId: String): Response> + + @PUT("$ENDPOINT/{overrideId}") + suspend fun updateWorkflowOverrideById( + @Path("overrideId") overrideId: String, + @Body request: UpdateWorkflowOverrideRequest + ): Response> + + @PUT("$ENDPOINT/workflows/{workflowId}/tenants/{tenantId}") + suspend fun updateWorkflowOverride( + @Path("workflowId") workflowId: String, + @Path("tenantId") tenantId: String, + @Body request: UpdateWorkflowOverrideRequest + ): Response> + + @DELETE("$ENDPOINT/{overrideId}") + suspend fun deleteWorkflowOverride(@Path("overrideId") overrideId: String): Response> +} diff --git a/src/main/kotlin/dto/request/CreateWorkflowOverrideRequest.kt b/src/main/kotlin/dto/request/CreateWorkflowOverrideRequest.kt new file mode 100644 index 0000000..d7d72dc --- /dev/null +++ b/src/main/kotlin/dto/request/CreateWorkflowOverrideRequest.kt @@ -0,0 +1,10 @@ +package dto.request + +import co.novu.dto.PreferenceSettings + +data class CreateWorkflowOverrideRequest( + var workflowId: String, + var tenantId: String, + var active: Boolean? = null, + var preferenceSettings: PreferenceSettings? = null +) diff --git a/src/main/kotlin/dto/request/GetWorkflowOverrideRequest.kt b/src/main/kotlin/dto/request/GetWorkflowOverrideRequest.kt new file mode 100644 index 0000000..52426cb --- /dev/null +++ b/src/main/kotlin/dto/request/GetWorkflowOverrideRequest.kt @@ -0,0 +1,6 @@ +package co.novu.dto.request + +data class GetWorkflowOverrideRequest( + var page: Int? = null, + var limit: Int? = null +) diff --git a/src/main/kotlin/dto/workflowoverrides/UpdateWorkflowOverrideRequest.kt b/src/main/kotlin/dto/workflowoverrides/UpdateWorkflowOverrideRequest.kt new file mode 100644 index 0000000..d2963fa --- /dev/null +++ b/src/main/kotlin/dto/workflowoverrides/UpdateWorkflowOverrideRequest.kt @@ -0,0 +1,8 @@ +package dto.workflowoverrides + +import co.novu.dto.PreferenceSettings + +data class UpdateWorkflowOverrideRequest( + var active: Boolean? = null, + var preferenceSettings: PreferenceSettings? = null +) diff --git a/src/main/kotlin/dto/workflowoverrides/WorkflowOverride.kt b/src/main/kotlin/dto/workflowoverrides/WorkflowOverride.kt new file mode 100644 index 0000000..e62e54d --- /dev/null +++ b/src/main/kotlin/dto/workflowoverrides/WorkflowOverride.kt @@ -0,0 +1,24 @@ +package dto.workflowoverrides + +import co.novu.dto.PreferenceSettings +import com.google.gson.annotations.SerializedName + +data class WorkflowOverride( + @SerializedName("_id") + var id: String? = null, + @SerializedName("_organizationId") + var organizationId: String? = null, + @SerializedName("_environmentId") + var environmentId: String? = null, + @SerializedName("_workflowId") + var workflowId: String? = null, + @SerializedName("_tenantId") + var tenantId: String? = null, + var active: Boolean? = null, + var preferenceSettings: PreferenceSettings? = null, + var deleted: Boolean? = null, + var deletedAt: String? = null, + var deletedBy: String? = null, + var createdAt: String? = null, + var updatedAt: String? = null +) diff --git a/src/main/kotlin/extensions/WorkflowOverrideExtensions.kt b/src/main/kotlin/extensions/WorkflowOverrideExtensions.kt new file mode 100644 index 0000000..a21ae5d --- /dev/null +++ b/src/main/kotlin/extensions/WorkflowOverrideExtensions.kt @@ -0,0 +1,51 @@ +package co.novu.extensions + +import co.novu.Novu +import co.novu.dto.request.GetWorkflowOverrideRequest +import co.novu.dto.response.PaginatedResponseWrapper +import co.novu.dto.response.ResponseWrapper +import co.novu.helpers.extractResponse +import dto.request.CreateWorkflowOverrideRequest +import dto.workflowoverrides.UpdateWorkflowOverrideRequest +import dto.workflowoverrides.WorkflowOverride +import mu.KotlinLogging + +private val logger = KotlinLogging.logger {} + +suspend fun Novu.createWorkflowOverride(request: CreateWorkflowOverrideRequest): ResponseWrapper? { + val response = workflowOverrideApi.createWorkflowOverride(request) + return response.extractResponse(logger) +} + +suspend fun Novu.getWorkflowOverrides(request: GetWorkflowOverrideRequest): PaginatedResponseWrapper? { + val params: MutableMap = HashMap() + request.page?.let { params["page"] = it } + request.limit?.let { params["limit"] = it } + val response = workflowOverrideApi.getWorkflowOverrides(params) + return response.extractResponse(logger) +} + +suspend fun Novu.getWorkflowOverride(workflowId: String, tenantId: String): ResponseWrapper? { + val response = workflowOverrideApi.getWorkflowOverride(workflowId, tenantId) + return response.extractResponse(logger) +} + +suspend fun Novu.getWorkflowOverrideById(overrideId: String): ResponseWrapper? { + val response = workflowOverrideApi.getWorkflowOverrideById(overrideId) + return response.extractResponse(logger) +} + +suspend fun Novu.updateWorkflowOverrideById(overrideId: String, request: UpdateWorkflowOverrideRequest): ResponseWrapper? { + val response = workflowOverrideApi.updateWorkflowOverrideById(overrideId, request) + return response.extractResponse(logger) +} + +suspend fun Novu.updateWorkflowOverride(workflowId: String, tenantId: String, request: UpdateWorkflowOverrideRequest): ResponseWrapper? { + val response = workflowOverrideApi.updateWorkflowOverride(workflowId, tenantId, request) + return response.extractResponse(logger) +} + +suspend fun Novu.deleteWorkflowOverride(overrideId: String): ResponseWrapper? { + val response = workflowOverrideApi.deleteWorkflowOverride(overrideId) + return response.extractResponse(logger) +} diff --git a/src/test/kotlin/WorkflowOverrideApiTest.kt b/src/test/kotlin/WorkflowOverrideApiTest.kt new file mode 100644 index 0000000..02129bc --- /dev/null +++ b/src/test/kotlin/WorkflowOverrideApiTest.kt @@ -0,0 +1,259 @@ +import co.novu.Novu +import co.novu.NovuConfig +import co.novu.dto.PreferenceSettings +import co.novu.dto.request.GetWorkflowOverrideRequest +import co.novu.dto.response.PaginatedResponseWrapper +import co.novu.dto.response.ResponseWrapper +import co.novu.extensions.createWorkflowOverride +import co.novu.extensions.deleteWorkflowOverride +import co.novu.extensions.getWorkflowOverride +import co.novu.extensions.getWorkflowOverrideById +import co.novu.extensions.getWorkflowOverrides +import co.novu.extensions.updateWorkflowOverride +import co.novu.extensions.updateWorkflowOverrideById +import com.google.gson.Gson +import dto.request.CreateWorkflowOverrideRequest +import dto.workflowoverrides.UpdateWorkflowOverrideRequest +import dto.workflowoverrides.WorkflowOverride +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class WorkflowOverrideApiTest { + private val mockWebServer = MockWebServer() + private val mockNovu = Novu( + NovuConfig(apiKey = "1245", backendUrl = mockWebServer.url("").toString()) + ) + + @Test + fun testCreateWorkflowOverride() = runTest { + val responseBody = ResponseWrapper( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(201).setBody(Gson().toJson(responseBody))) + val requestBody = CreateWorkflowOverrideRequest( + workflowId = "wId", + tenantId = "tId" + ) + requestBody.active = true + requestBody.preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + val result = mockNovu.createWorkflowOverride(requestBody) + val request = mockWebServer.takeRequest() + + assert(request.method == "POST") + assert(request.path == "/workflow-overrides") + assert(request.body.readUtf8() == Gson().toJson(requestBody)) + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testGetWorkflowOverrides() = runTest { + val responseBody = PaginatedResponseWrapper( + data = listOf( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + val requestBody = GetWorkflowOverrideRequest( + page = 1, + limit = 10 + ) + val result = mockNovu.getWorkflowOverrides(requestBody) + val request = mockWebServer.takeRequest() + + assert(request.method == "GET") + assert(request.path == "/workflow-overrides?limit=${requestBody.limit}&page=${requestBody.page}") + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testGetWorkflowOverride() = runTest { + val responseBody = ResponseWrapper( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + + val workflowId = "wId" + val tenantId = "tId" + val result = mockNovu.getWorkflowOverride(workflowId, tenantId) + val request = mockWebServer.takeRequest() + + assert(request.method == "GET") + assert(request.path == "/workflow-overrides/workflows/$workflowId/tenants/$tenantId") + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testGetWorkflowOverrideById() = runTest { + val responseBody = ResponseWrapper( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + + val overrideId = "oId" + val result = mockNovu.getWorkflowOverrideById(overrideId) + val request = mockWebServer.takeRequest() + + assert(request.method == "GET") + assert(request.path == "/workflow-overrides/$overrideId") + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testUpdateWorkflowOverrideById() = runTest { + val responseBody = ResponseWrapper( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + + val requestBody = UpdateWorkflowOverrideRequest( + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + + val overrideId = "oId" + val result = mockNovu.updateWorkflowOverrideById(overrideId, requestBody) + val request = mockWebServer.takeRequest() + + assert(request.method == "PUT") + assert(request.path == "/workflow-overrides/$overrideId") + assert(request.body.readUtf8() == Gson().toJson(requestBody)) + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testUpdateWorkflowOverride() = runTest { + val responseBody = ResponseWrapper( + WorkflowOverride( + id = "id", + tenantId = "tId", + workflowId = "wId", + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + ) + + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + + val workflowId = "wId" + val tenantId = "tId" + val requestBody = UpdateWorkflowOverrideRequest( + active = true, + preferenceSettings = PreferenceSettings( + email = true, + sms = true, + push = true, + inApp = true, + chat = true + ) + ) + + val result = mockNovu.updateWorkflowOverride(workflowId, tenantId, requestBody) + val request = mockWebServer.takeRequest() + + assert(request.method == "PUT") + assert(request.path == "/workflow-overrides/workflows/$workflowId/tenants/$tenantId") + assert(request.body.readUtf8() == Gson().toJson(requestBody)) + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } + + @Test + fun testDeleteWorkflow() = runTest { + val responseBody = ResponseWrapper(true) + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(responseBody))) + + val overrideId = "oId" + val result = mockNovu.deleteWorkflowOverride(overrideId) + val request = mockWebServer.takeRequest() + + assert(request.method == "DELETE") + assert(request.path == "/workflow-overrides/$overrideId") + assert(Gson().toJson(responseBody) == Gson().toJson(result)) + } +} From c94b51629e236318bb4ed876c097b43f44875594 Mon Sep 17 00:00:00 2001 From: MayorJay Date: Sun, 11 Feb 2024 23:48:57 +0000 Subject: [PATCH 2/2] feat: Update Readme --- Readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Readme.md b/Readme.md index 4749473..af03add 100644 --- a/Readme.md +++ b/Readme.md @@ -218,6 +218,16 @@ The client methods map directly to the Novu API endpoints. Here is a list of all - `fetchMembersOfOrganization()` - `updateOrganizationBrand(body)` +### Workflow Override + +- `createWorkflowOverride(createWorkflowOverrideRequest)` +- `getWorkflowOverrides(getWorkflowOverrideRequest)` +- `getWorkflowOverride(workflowId, tenantId)` +- `getWorkflowOverrideById(overrideId)` +- `updateWorkflowOverride(workflowId, tenantId)` +- `updateWorkflowOverrideById(overrideId)` +- `deleteWorkflowOverride(overrideId)` + ### For more information about these methods and their parameters, see the [API documentation](https://docs.novu.co/api-reference/overview). ## Contributing