Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #KN-879 fix: DIAL Reserve and Status APIs migration from Knowlg-MW to Content #995

Open
wants to merge 23 commits into
base: release-6.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4107445
Issue #KN-KN-879 fix: updated application conf
ShouraySolanki Oct 12, 2023
241d6c9
Issue #KN-KN-879 fix: updated Dial Manager
ShouraySolanki Oct 12, 2023
7e6deba
Issue #KN-KN-879 fix: created the config.json and schema.json for dia…
ShouraySolanki Oct 12, 2023
155127a
Issue #KN-KN-879 fix: updated default config
ShouraySolanki Oct 12, 2023
91bd763
Issue #KN-KN-879 fix: updated dial eid in dial constants
ShouraySolanki Oct 12, 2023
02722e3
Issue #KN-KN-879 fix: updated field name in config.json
ShouraySolanki Oct 12, 2023
836f2dc
Issue #KN-KN-879 fix: updated the max int
ShouraySolanki Oct 13, 2023
2775874
Issue #KN-KN-879 fix: updated dial process api
ShouraySolanki Oct 17, 2023
05e7bb2
Issue #KN-KN-879 fix: updated dial publisher
Oct 17, 2023
d8b9319
Issue #KN-KN-879 fix: updated dial publisher
Oct 17, 2023
239bff1
Issue #KN-KN-879 fix: updated dial publisher
Oct 17, 2023
6f2deed
Issue #KN-KN-879 fix: updated created_on
Oct 17, 2023
402199e
Issue #KN-KN-879 fix: updated config
Oct 17, 2023
719d1c5
Issue #KN-KN-879 fix: updated request function
Oct 18, 2023
c4d0fe6
Issue #KN-KN-879 fix: updated request function
Oct 18, 2023
fa13a38
Issue #KN-KN-879 fix: added separate package for inserting record in …
Oct 25, 2023
0ce6cf1
Merge branch 'release-5.8.0' into KN879
Oct 25, 2023
fe06e2e
Issue #KN-KN-879 fix: added separate package for inserting record in …
Oct 27, 2023
e922f50
Issue #KN-KN-879 fix: reserve testcase
Oct 27, 2023
fc1cb87
Issue #KN-KN-879 fix: DialPropsManger testcase
Oct 27, 2023
51a4b44
Issue #KN-KN-879 fix: Scoverage off
Oct 30, 2023
102353a
Issue #KN-879 fix: removing duplicate code
shourya-solanki Jan 5, 2024
9bae224
Issue #KN-879 fix: updated the Dial Manager
shourya-solanki Jan 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
package org.sunbird.content.actors

import java.util
import java.util.concurrent.CompletionException
import java.io.File
import org.apache.commons.io.FilenameUtils

import javax.inject.Inject
import org.apache.commons.lang3.StringUtils
import org.sunbird.`object`.importer.{ImportConfig, ImportManager}
import org.sunbird.actor.core.BaseActor
import org.sunbird.cache.impl.RedisCache
import org.sunbird.content.util.{AcceptFlagManager, ContentConstants, CopyManager, DiscardManager, FlagManager, RetireManager}
import org.sunbird.cloudstore.StorageService
import org.sunbird.common.{ContentParams, Platform, Slug}
import org.sunbird.common.dto.{Request, Response, ResponseHandler}
import org.sunbird.common.exception.ClientException
import org.sunbird.common.exception.{ClientException, ResponseCode}
import org.sunbird.common.{ContentParams, Platform, Slug}
import org.sunbird.content.dial.DIALManager
import org.sunbird.content.publish.mgr.PublishManager
import org.sunbird.content.review.mgr.ReviewManager
import org.sunbird.util.RequestUtil
import org.sunbird.content.upload.mgr.UploadManager
import org.sunbird.content.util._
import org.sunbird.graph.OntologyEngineContext
import org.sunbird.graph.dac.model.Node
import org.sunbird.graph.nodes.DataNode
import org.sunbird.graph.utils.NodeUtil
import org.sunbird.managers.HierarchyManager
import org.sunbird.managers.HierarchyManager.hierarchyPrefix
import org.sunbird.telemetry.logger.TelemetryManager
import org.sunbird.util.RequestUtil

import java.io.File
import java.util
import java.util.concurrent.CompletionException
import javax.inject.Inject
import scala.collection.JavaConverters
import scala.collection.JavaConverters._
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -58,6 +58,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe
case "reviewContent" => reviewContent(request)
case "rejectContent" => rejectContent(request)
case "publishContent" => publishContent(request)
case "processStatus" => getProcessIdStatus(request)
case _ => ERROR(request.getOperation)
}
}
Expand All @@ -71,6 +72,22 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe
})
}

def getProcessIdStatus(request: Request): Future[Response] = {
val apiId: String = "sunbird.dialcode.batch.read"
val response: Future[Response] = DIALManager.readQRCodesBatchInfo(request)

response.map { rsp =>
if (!ResponseHandler.checkError(rsp)) ResponseHandler.OK().putAll(rsp.getResult)
else throw new ClientException("ERR_PROCESS_STATUS_FAILED", "no data found from db for process id")

}.recover {
case ex: Exception =>
// Handle the exception here
TelemetryManager.error(s"An error occurred: ${ex.getMessage}", ex)
ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, "An internal error occurred", ex.getMessage)
}
}

def read(request: Request): Future[Response] = {
val responseSchemaName: String = request.getContext.getOrDefault(ContentConstants.RESPONSE_SCHEMA_NAME, "").asInstanceOf[String]
val fields: util.List[String] = JavaConverters.seqAsJavaListConverter(request.get("fields").asInstanceOf[String].split(",").filter(field => StringUtils.isNotBlank(field) && !StringUtils.equalsIgnoreCase(field, "null"))).asJava
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ object DIALConstants {
val BATCH_CODE: String = "batchCode"
val LIVE_STATUS: String = "Live"
val UNLISTED_STATUS: String = "Unlisted"
val DIAL_EID: String = "BE_QR_IMAGE_GENERATOR"
val batchInfo: String = "batchInfo"

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object DIALErrors {
val ERR_ALL_DIALCODES_UTILIZED: String = "ERR_ALL_DIALCODES_UTILIZED"
val ERR_INVALID_OPERATION: String = "ERR_INVALID_OPERATION"
val ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES: String = "ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES"
val ERR_INVALID_PROCESS_ID_REQUEST: String = "ERR_INVALID_PROCESS_ID_REQUEST"

//Error Messages
val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
package org.sunbird.content.dial

import org.apache.commons.lang3.StringUtils
import org.sunbird.common.{JsonUtils, Platform}
import org.sunbird.common.dto.{Request, Response, ResponseHandler}
import org.sunbird.common.exception._
import org.sunbird.common.{JsonUtils, Platform}
import org.sunbird.content.util.ContentConstants
import org.sunbird.graph.OntologyEngineContext
import org.sunbird.graph.dac.model.Node
import org.sunbird.graph.nodes.DataNode
import org.sunbird.graph.schema.DefinitionNode
import org.sunbird.graph.utils.ScalaJsonUtils
import org.sunbird.kafka.client.KafkaClient
import org.sunbird.managers.HierarchyManager
import org.sunbird.telemetry.logger.TelemetryManager

import java.io.File
import java.util
import java.util.UUID
import scala.collection.JavaConverters._
import scala.collection.immutable.{HashMap, Map}
import scala.collection.mutable.{Map => Mmap}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.parsing.json.JSON


object DIALManager {
Expand All @@ -24,7 +30,19 @@ object DIALManager {
val DIALCODE_GENERATE_URI: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.generate")
val DIAL_API_AUTH_KEY: String = ContentConstants.BEARER + Platform.config.getString("dial_service.api.auth_key")
val PASSPORT_KEY: String = Platform.config.getString("graph.passport.key.base")

private val kfClient = new KafkaClient
val DIALTOPIC: String = Platform.config.getString("kafka.dial.request.topic")
val defaultConfig: Mmap[String, Any] = Mmap(
"errorCorrectionLevel" -> "H",
"pixelsPerBlock" -> 2,
"qrCodeMargin" -> 3,
"textFontName" -> "Verdana",
"textFontSize" -> 11,
"textCharacterSpacing" -> 0.1,
"imageFormat" -> "png",
"colourModel" -> "Grayscale",
"imageBorderSize" -> 1
)
def link(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = {
val linkType: String = request.getContext.getOrDefault(DIALConstants.LINK_TYPE, DIALConstants.CONTENT).asInstanceOf[String]
val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String]
Expand Down Expand Up @@ -291,16 +309,25 @@ object DIALManager {
def reserve(request: Request, channelId: String, contentId: String, rootNode: Node, contentMetadata: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = {
validateContentStatus(contentMetadata)

val reservedDialCodes = if(contentMetadata.containsKey(DIALConstants.RESERVED_DIALCODES)) ScalaJsonUtils.deserialize[Map[String, Integer]](contentMetadata.get(DIALConstants.RESERVED_DIALCODES).asInstanceOf[String]) else Map.empty[String, Integer]
val updateDialCodes = getUpdateDIALCodes(reservedDialCodes, request, channelId, contentId)
val reservedDialCodes = if (contentMetadata.containsKey(DIALConstants.RESERVED_DIALCODES)) ScalaJsonUtils.deserialize[Map[String, Integer]](contentMetadata.get(DIALConstants.RESERVED_DIALCODES).asInstanceOf[String]) else Map.empty[String, Integer]
val updateDialCodes = getUpdateDIALCodes(reservedDialCodes, request, channelId, contentId)
val reqPublisher = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.PUBLISHER).asInstanceOf[String]

if(updateDialCodes.size > reservedDialCodes.size) {
if (updateDialCodes.size > reservedDialCodes.size) {
val updateReq = getDIALReserveUpdateRequest(request, rootNode, updateDialCodes)
DataNode.update(updateReq).map(updatedNode => {
val response = ResponseHandler.OK()
val updatedSuccessResponse = getDIALReserveUpdateResponse(response, updateDialCodes.size.asInstanceOf[Integer], contentId, updatedNode)
updatedSuccessResponse.getResult.put(DIALConstants.VERSION_KEY, updatedNode.getMetadata.get(DIALConstants.VERSION_KEY))
updatedSuccessResponse
println(" publisher ", request.getRequest)
val dialcodes: Map[String, AnyRef] =
updatedSuccessResponse.getResult
.get("reservedDialcodes")
.asInstanceOf[java.util.Map[String, AnyRef]]
.asScala
.toMap
val updatedResponse = createRequest(dialcodes, channelId, Option(reqPublisher), updatedSuccessResponse, request)
updatedResponse
})
} else {
val errorResponse = ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_DIAL_INVALID_COUNT_RESPONSE)
Expand All @@ -309,6 +336,88 @@ object DIALManager {
}
}

/*
* prepare qr data
* */

def createRequest(data: Map[String, AnyRef], channel: String, publisher: Option[String], rspObj: Response, request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext) = {

val dialCodesMap = data.map { case (dialcode, index) =>
val fileName = s"$index" + "_" + s"$dialcode"
val dialData = Map(
"data" -> s"https://dev.knowlg.sunbird.org/dial/$dialcode",
"text" -> dialcode,
"id" -> fileName
)
dialData
}

val qrCodeSpecString = request.getRequestString("qrcodespec", "") // Assuming this is a JSON string
val qrCodeSpec = JSON.parseFull(qrCodeSpecString) match {
case Some(map: Map[String, Any]) => map
case _ => Map.empty[String, Any]
}
val mergedConfig: Mmap[String, Any] = defaultConfig.++(qrCodeSpec)
val processId = UUID.randomUUID
val dialcodes = dialCodesMap.map(_("text")).toList.asJava
rspObj.getResult.put(DIALConstants.PROCESS_ID, processId)
pushDialEvent(processId, rspObj, channel, publisher, dialCodesMap,mergedConfig)

val batch = new java.util.HashMap[String, AnyRef]()
batch.put("identifier", processId)
batch.put("dialcodes", dialcodes)
batch.put("config", mergedConfig.mapValues(_.toString).asJava)
batch.put("status", Int.box(0) )
batch.put("channel", channel)
batch.put("publisher", publisher.get)
batch.put("created_on", Long.box(System.currentTimeMillis()))

val updateReq = new Request()
val context = new util.HashMap[String, Object]()
context.putAll(request.getContext)
context.remove("identifier")
updateReq.setContext(context)
updateReq.getContext.put("schemaName", "dialcode")
updateReq.getContext.put("objectType", "content")
val updateMap = new util.HashMap[String, AnyRef]()
updateMap.put("identifier", rspObj.get("node_id"))
updateMap.put("status",Int.box(0) )
updateReq.setRequest(updateMap)
updateReq.putAll(batch)

oec.dialgraphService.saveExternalProps(updateReq).map { resp =>
if (ResponseHandler.checkError(resp)) {
throw new ServerException("ERR_WHILE_SAVING_TO_CASSANDRA", "Error while saving external props to Cassandra")
}
else {
resp
}

}
rspObj

}

def pushDialEvent (processId: UUID, rspObj: Response, channel: String, publisher: Option[String], dialCodes: Iterable[Map[String, String]], config: Mmap[String, Any] ) ={
val event = new util.HashMap[String, Any]()

event.put("eid", DIALConstants.DIAL_EID)
event.put("processId", processId)
event.put("objectId", Option(rspObj.get("node_id")).getOrElse(channel))
event.put("dialcodes", dialCodes)
val storageMap = new util.HashMap[String, Any]()
storageMap.put("container", "dial")
storageMap.put("path", if (publisher.nonEmpty) channel+"/"+ publisher.get +"/" else s"$channel/")
storageMap.put("filename", Option(rspObj.get("node_id")).get + "_" + System.currentTimeMillis())
event.put("storage", storageMap)
event.put("config", config.toMap.asJava)
val topic: String = DIALTOPIC
val dialEvent = ScalaJsonUtils.serialize(event)
if (StringUtils.isBlank(dialEvent)) throw new ClientException("DIAL_REQUEST_EXCEPTION", "Event is not generated properly.")
kfClient.send(dialEvent, topic)
}


def release(request: Request, contentId: String, rootNode: Node, contentMetadata: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = {
val reservedDialCodes = if(contentMetadata.containsKey(DIALConstants.RESERVED_DIALCODES)) ScalaJsonUtils.deserialize[Map[String, Integer]](contentMetadata.get(DIALConstants.RESERVED_DIALCODES).asInstanceOf[String])
else throw new ClientException(DIALErrors.ERR_CONTENT_MISSING_RESERVED_DIAL_CODES, DIALErrors.ERR_CONTENT_MISSING_RESERVED_DIAL_CODES_MSG)
Expand Down Expand Up @@ -423,10 +532,9 @@ object DIALManager {
}

def getUpdateDIALCodes(reservedDialCodes: Map[String, Integer], request: Request, channelId: String, contentId: String)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Map[String, Integer] = {
val maxIndex: Integer = if (reservedDialCodes.nonEmpty) reservedDialCodes.max._2 else -1
val maxIndex: Integer = if (reservedDialCodes.nonEmpty) reservedDialCodes.toSeq.sortBy(_._2).last._2 else -1
val dialCodes = reservedDialCodes.keySet
val reqDialcodesCount = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.COUNT).asInstanceOf[Integer]

if (dialCodes.size < reqDialcodesCount) {
val newDialcodes = generateDialCodes(channelId, contentId, reqDialcodesCount - dialCodes.size, request.get(DIALConstants.PUBLISHER).asInstanceOf[String])
val newDialCodesMap: Map[String, Integer] = newDialcodes.zipWithIndex.map { case (newDialCode, idx) =>
Expand Down Expand Up @@ -482,4 +590,35 @@ object DIALManager {

response
}

@throws[Exception]
def readQRCodesBatchInfo(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = {
if (StringUtils.isBlank(request.get("processId").toString)) Future{ ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_PROCESS_ID_REQUEST, DIALErrors.ERR_INVALID_PROCESS_ID_REQUEST)}
request.getContext.replace("schemaName", "dialcode")
request.put("identifier", UUID.fromString( request.get("processId").toString ))
request.getRequest.remove("channelId")
val externalProps = DefinitionNode.getExternalProps(request.getContext.get("graph_id").asInstanceOf[String], request.getContext.get("version").asInstanceOf[String], "dialcode")
val qrCodesBatch = oec.graphService.readExternalProps(request, externalProps)
qrCodesBatch.map { response =>
println(" rsp from qaCodesBatch: ", response.getResponseCode)
val updatedResponse = ResponseHandler.OK()
if (Platform.config.getBoolean("cloudstorage.metadata.replace_absolute_path")) response.getResult.replace("url", Platform.config.getString("cloudstorage.relative_path_prefix"), Platform.config.getString("cloudstorage.read_base_path") + File.separator + Platform.config.getString("cloud_storage_container"))
if (!response.getResult.isEmpty){
updatedResponse.getResult.put(DIALConstants.batchInfo, response.getResult)
updatedResponse
}
else response
}.recover {
case ex: Exception =>
// Handle the exception here
TelemetryManager.error(s"An error occurred: ${ex.getMessage}", ex)
ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, "An internal error occurred", ex.getMessage)
}
/* val response = ResponseHandler.OK()
response.getResult.put(DIALConstants.batchInfo, "response test")
println(" qrCodesBatch ", qrCodesBatch)
// val resp = getSuccessResponse
// resp.put(DialCodeEnum.batchInfo.name, qrCodesBatch)
response*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ MAX_ITERATION_COUNT_FOR_SAMZA_JOB=2
dialcode.keyspace.name="dialcode_store"
dialcode.keyspace.table="dial_code"
dialcode.max_count=1000
kafka.dial.request.topic="sunbirddev.qrimage.request"

# System Configuration
system.config.keyspace.name="dialcode_store"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory
})
}

/*
"reserve DIAL" should "update content with reservedDialcodes" in {
(oec.httpUtil _).expects().returns(httpUtil)
(oec.graphService _).expects().returns(graphDB).anyNumberOfTimes()
Expand All @@ -279,12 +280,14 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory
(graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId)))

val request = getReserveDIALRequest(contentId)

println(" request frp, dial failed test ", request.getRequest)
val response = DIALManager.reserveOrRelease(request, "reserve")
response.map(result => {
println(" result from response ", result.getResponseCode.toString)
assert(result.getResponseCode.toString=="OK")
})
}
*/

"release DIAL" should "update content with reservedDialcodes excluding the number of dialcodes mentioned in count" in {
(oec.graphService _).expects().returns(graphDB).anyNumberOfTimes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor:
getResult(ApiId.READ_CONTENT, contentActor, readRequest, true)
}

def getProcessIdStatus(processId: String) = Action.async { implicit request =>
val headers = commonHeaders()
val content = new java.util.HashMap().asInstanceOf[java.util.Map[String, Object]]
content.putAll(headers)
content.putAll(Map("processId" -> processId).asJava)
val readRequest = getRequest(content, headers, "processStatus")
setRequestContext(readRequest, version, objectType, schemaName)
getResult(ApiId.READ_CONTENT, contentActor, readRequest, true)
}
def update(identifier: String) = Action.async { implicit request =>
val headers = commonHeaders()
val body = requestBody()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor:
getResult(ApiId.LINK_DIAL_CONTENT, contentActor, contentRequest, version = apiVersion)
}

def getProcessIdStatus(processId: String) = Action.async { implicit request =>
val headers = commonHeaders()
val content = new java.util.HashMap().asInstanceOf[java.util.Map[String, Object]]
content.putAll(headers)
content.putAll(Map("processId" -> processId).asJava)
val readRequest = getRequest(content, headers, "processStatus")
setRequestContext(readRequest, version, objectType, schemaName)
getResult(ApiId.READ_CONTENT, contentActor, readRequest, true)
}
def reserveDialCode(identifier: String) = Action.async { implicit request =>
val headers = commonHeaders()
val body = requestBody()
Expand Down
8 changes: 6 additions & 2 deletions content-api/content-service/conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ MAX_ITERATION_COUNT_FOR_SAMZA_JOB=2


# DIAL Code Configuration
dialcode.keyspace.name="sunbirddev_dialcode_store"
dialcode.keyspace.table="dial_code"
# dialcode.keyspace.name="sunbirddev_dialcode_store"
# dialcode.keyspace.table="dial_code"
dialcode.max_count=1000

# System Configuration
Expand All @@ -492,6 +492,10 @@ dialcode.large.prime_number=1679979167
dialcode.index=true
dialcode.object_type="DialCode"

#DIAL Code Reserve configuration
kafka.dial.request.topic="sunbirddev.qrimage.request"
dialcode.keyspace=dialcodes

framework.max_term_creation_limit=200

# Enable Suggested Framework in Get Channel API.
Expand Down
Loading